Skip to content

Commit

Permalink
Merge pull request #2416 from neutrinoceros/numpy2_compat
Browse files Browse the repository at this point in the history
ENH: avoid deprecation warnings from numpy 2
  • Loading branch information
takluyver committed May 8, 2024
2 parents 25c55e5 + 12c62ab commit b2a83a0
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 8 deletions.
40 changes: 33 additions & 7 deletions h5py/_hl/dataset.py
Expand Up @@ -213,10 +213,16 @@ def __len__(self):
"""
return len(self._dset)

def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=True):
if copy is False:
raise ValueError(
f"AstypeWrapper.__array__ received {copy=} "
f"but memory allocation cannot be avoided on read"
)

data = self[:]
if dtype is not None:
data = data.astype(dtype)
return data.astype(dtype, copy=False)
return data


Expand Down Expand Up @@ -251,7 +257,16 @@ def __len__(self):
"""
return len(self._dset)

def __array__(self):
def __array__(self, dtype=None, copy=True):
if dtype not in (None, object):
raise TypeError(
"AsStrWrapper.__array__ doesn't support the dtype argument"
)
if copy is False:
raise ValueError(
f"AsStrWrapper.__array__ received {copy=} "
f"but memory allocation cannot be avoided on read"
)
return numpy.array([
b.decode(self.encoding, self.errors) for b in self._dset
], dtype=object).reshape(self._dset.shape)
Expand All @@ -268,11 +283,17 @@ def __init__(self, dset, prior_dtype, names):
names = [names]
self.read_dtype = readtime_dtype(prior_dtype, names)

def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=True):
if copy is False:
raise ValueError(
f"FieldsWrapper.__array__ received {copy=} "
f"but memory allocation cannot be avoided on read"
)
data = self[:]
if dtype is not None:
data = data.astype(dtype)
return data
return data.astype(dtype, copy=False)
else:
return data

def __getitem__(self, args):
data = self._dset.__getitem__(args, new_dtype=self.read_dtype)
Expand Down Expand Up @@ -1049,11 +1070,16 @@ def write_direct(self, source, source_sel=None, dest_sel=None):
self.id.write(mspace, fspace, source, dxpl=self._dxpl)

@with_phil
def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=True):
""" Create a Numpy array containing the whole dataset. DON'T THINK
THIS MEANS DATASETS ARE INTERCHANGEABLE WITH ARRAYS. For one thing,
you have to read the whole dataset every time this method is called.
"""
if copy is False:
raise ValueError(
f"Dataset.__array__ received {copy=} "
f"but memory allocation cannot be avoided on read"
)
arr = numpy.zeros(self.shape, dtype=self.dtype if dtype is None else dtype)

# Special case for (0,)*-shape datasets
Expand Down
38 changes: 38 additions & 0 deletions h5py/tests/test_dataset.py
Expand Up @@ -1785,6 +1785,13 @@ def test_non_contiguous_arrays(self):

assert all(self.f['nc2'][0] == y[::2]), f"{self.f['nc2'][0]} != {y[::2]}"

def test_asstr_array_dtype(self):
dt = h5py.string_dtype(encoding='ascii')
fill_value = b'bar'
ds = self.f.create_dataset('x', (100,), dtype=dt, fillvalue=fill_value)
with pytest.raises(TypeError):
np.array(ds.asstr(), dtype=int)


class TestLowOpen(BaseDataset):

Expand Down Expand Up @@ -2013,3 +2020,34 @@ def test_virtual_prefix_require(self):
self.assertEqual(virtual_prefix, virtual_prefix_readback)
self.assertIsInstance(dset, Dataset)
self.assertEqual(dset.shape, (10, 3))



COPY_IF_NEEDED = False if np.__version__.startswith("1.") else None

VIEW_GETTERS = {
"ds": lambda ds: ds,
"astype": lambda ds: ds.astype(dtype=object),
"asstr": lambda ds: ds.asstr(),
}

@pytest.mark.parametrize("copy", [True, COPY_IF_NEEDED])
@pytest.mark.parametrize("view_getter", VIEW_GETTERS.values(), ids=VIEW_GETTERS.keys())
def test_array_copy(view_getter, copy, writable_file):
dt = h5py.string_dtype(encoding='ascii')
fill_value = b'bar'
ds = writable_file.create_dataset('x', (10,), dtype=dt, fillvalue=fill_value)
np.array(view_getter(ds), copy=copy)

@pytest.mark.skipif(
np.__version__.startswith("1."),
reason="forbidding copies requires numpy 2",
)
@pytest.mark.parametrize("view_getter", VIEW_GETTERS.values(), ids=VIEW_GETTERS.keys())
def test_array_copy_false(view_getter, writable_file):
dt = h5py.string_dtype(encoding='ascii')
fill_value = b'bar'
ds = writable_file.create_dataset('x', (10,), dtype=dt, fillvalue=fill_value)
view = view_getter(ds)
with pytest.raises(ValueError):
np.array(view, copy=False)
2 changes: 1 addition & 1 deletion h5py/tests/test_dtype.py
Expand Up @@ -131,7 +131,7 @@ class TestEmptyVlen(TestCase):
def test_write_empty_vlen(self):
fname = self.mktemp()
with h5py.File(fname, 'w') as f:
d = np.core.records.fromarrays([[], []], names='a,b', formats='|V16,O')
d = np.rec.fromarrays([[], []], names='a,b', formats='|V16,O')
dset = f.create_dataset('test', data=d, dtype=[('a', '|V16'), ('b', h5py.special_dtype(vlen=np.float64))])
self.assertEqual(dset.size, 0)

Expand Down

0 comments on commit b2a83a0

Please sign in to comment.