|
13 | 13 | from zarr._storage.store import _prefix_to_attrs_key, assert_zarr_v3_api_available
|
14 | 14 | from zarr.attrs import Attributes
|
15 | 15 | from zarr.codecs import AsType, get_codec
|
| 16 | +from zarr.context import Context |
16 | 17 | from zarr.errors import ArrayNotFoundError, ReadOnlyError, ArrayIndexError
|
17 | 18 | from zarr.indexing import (
|
18 | 19 | BasicIndexer,
|
|
41 | 42 | normalize_store_arg,
|
42 | 43 | )
|
43 | 44 | from zarr.util import (
|
| 45 | + ConstantMap, |
44 | 46 | all_equal,
|
45 | 47 | InfoReporter,
|
46 | 48 | check_array_shape,
|
@@ -1275,24 +1277,14 @@ def _get_selection(self, indexer, out=None, fields=None):
|
1275 | 1277 | check_array_shape('out', out, out_shape)
|
1276 | 1278 |
|
1277 | 1279 | # iterate over chunks
|
1278 |
| - if ( |
1279 |
| - not hasattr(self.chunk_store, "getitems") and not ( |
1280 |
| - hasattr(self.chunk_store, "get_partial_values") and |
1281 |
| - self.chunk_store.supports_efficient_get_partial_values |
1282 |
| - ) |
1283 |
| - ) or any(map(lambda x: x == 0, self.shape)): |
1284 |
| - # sequentially get one key at a time from storage |
1285 |
| - for chunk_coords, chunk_selection, out_selection in indexer: |
1286 | 1280 |
|
1287 |
| - # load chunk selection into output array |
1288 |
| - self._chunk_getitem(chunk_coords, chunk_selection, out, out_selection, |
1289 |
| - drop_axes=indexer.drop_axes, fields=fields) |
1290 |
| - else: |
| 1281 | + if math.prod(out_shape) > 0: |
1291 | 1282 | # allow storage to get multiple items at once
|
1292 | 1283 | lchunk_coords, lchunk_selection, lout_selection = zip(*indexer)
|
1293 |
| - self._chunk_getitems(lchunk_coords, lchunk_selection, out, lout_selection, |
1294 |
| - drop_axes=indexer.drop_axes, fields=fields) |
1295 |
| - |
| 1284 | + self._chunk_getitems( |
| 1285 | + lchunk_coords, lchunk_selection, out, lout_selection, |
| 1286 | + drop_axes=indexer.drop_axes, fields=fields |
| 1287 | + ) |
1296 | 1288 | if out.shape:
|
1297 | 1289 | return out
|
1298 | 1290 | else:
|
@@ -1963,68 +1955,36 @@ def _process_chunk(
|
1963 | 1955 | # store selected data in output
|
1964 | 1956 | out[out_selection] = tmp
|
1965 | 1957 |
|
1966 |
| - def _chunk_getitem(self, chunk_coords, chunk_selection, out, out_selection, |
1967 |
| - drop_axes=None, fields=None): |
1968 |
| - """Obtain part or whole of a chunk. |
| 1958 | + def _chunk_getitems(self, lchunk_coords, lchunk_selection, out, lout_selection, |
| 1959 | + drop_axes=None, fields=None): |
| 1960 | + """Obtain part or whole of chunks. |
1969 | 1961 |
|
1970 | 1962 | Parameters
|
1971 | 1963 | ----------
|
1972 |
| - chunk_coords : tuple of ints |
1973 |
| - Indices of the chunk. |
1974 |
| - chunk_selection : selection |
1975 |
| - Location of region within the chunk to extract. |
| 1964 | + chunk_coords : list of tuple of ints |
| 1965 | + Indices of the chunks. |
| 1966 | + chunk_selection : list of selections |
| 1967 | + Location of region within the chunks to extract. |
1976 | 1968 | out : ndarray
|
1977 | 1969 | Array to store result in.
|
1978 |
| - out_selection : selection |
1979 |
| - Location of region within output array to store results in. |
| 1970 | + out_selection : list of selections |
| 1971 | + Location of regions within output array to store results in. |
1980 | 1972 | drop_axes : tuple of ints
|
1981 | 1973 | Axes to squeeze out of the chunk.
|
1982 | 1974 | fields
|
1983 | 1975 | TODO
|
1984 |
| -
|
1985 | 1976 | """
|
1986 |
| - out_is_ndarray = True |
1987 |
| - try: |
1988 |
| - out = ensure_ndarray_like(out) |
1989 |
| - except TypeError: |
1990 |
| - out_is_ndarray = False |
1991 |
| - |
1992 |
| - assert len(chunk_coords) == len(self._cdata_shape) |
1993 |
| - |
1994 |
| - # obtain key for chunk |
1995 |
| - ckey = self._chunk_key(chunk_coords) |
1996 | 1977 |
|
1997 |
| - try: |
1998 |
| - # obtain compressed data for chunk |
1999 |
| - cdata = self.chunk_store[ckey] |
2000 |
| - |
2001 |
| - except KeyError: |
2002 |
| - # chunk not initialized |
2003 |
| - if self._fill_value is not None: |
2004 |
| - if fields: |
2005 |
| - fill_value = self._fill_value[fields] |
2006 |
| - else: |
2007 |
| - fill_value = self._fill_value |
2008 |
| - out[out_selection] = fill_value |
2009 |
| - |
2010 |
| - else: |
2011 |
| - self._process_chunk(out, cdata, chunk_selection, drop_axes, |
2012 |
| - out_is_ndarray, fields, out_selection) |
2013 |
| - |
2014 |
| - def _chunk_getitems(self, lchunk_coords, lchunk_selection, out, lout_selection, |
2015 |
| - drop_axes=None, fields=None): |
2016 |
| - """As _chunk_getitem, but for lists of chunks |
2017 |
| -
|
2018 |
| - This gets called where the storage supports ``getitems``, so that |
2019 |
| - it can decide how to fetch the keys, allowing concurrency. |
2020 |
| - """ |
2021 | 1978 | out_is_ndarray = True
|
2022 | 1979 | try:
|
2023 | 1980 | out = ensure_ndarray_like(out)
|
2024 | 1981 | except TypeError: # pragma: no cover
|
2025 | 1982 | out_is_ndarray = False
|
2026 | 1983 |
|
| 1984 | + # Keys to retrieve |
2027 | 1985 | ckeys = [self._chunk_key(ch) for ch in lchunk_coords]
|
| 1986 | + |
| 1987 | + # Check if we can do a partial read |
2028 | 1988 | if (
|
2029 | 1989 | self._partial_decompress
|
2030 | 1990 | and self._compressor
|
@@ -2056,13 +2016,17 @@ def _chunk_getitems(self, lchunk_coords, lchunk_selection, out, lout_selection,
|
2056 | 2016 | for ckey in ckeys
|
2057 | 2017 | if ckey in self.chunk_store
|
2058 | 2018 | }
|
| 2019 | + elif hasattr(self.chunk_store, "get_partial_values"): |
| 2020 | + partial_read_decode = False |
| 2021 | + values = self.chunk_store.get_partial_values([(ckey, (0, None)) for ckey in ckeys]) |
| 2022 | + cdatas = {key: value for key, value in zip(ckeys, values) if value is not None} |
2059 | 2023 | else:
|
2060 | 2024 | partial_read_decode = False
|
2061 |
| - if not hasattr(self.chunk_store, "getitems"): |
2062 |
| - values = self.chunk_store.get_partial_values([(ckey, (0, None)) for ckey in ckeys]) |
2063 |
| - cdatas = {key: value for key, value in zip(ckeys, values) if value is not None} |
2064 |
| - else: |
2065 |
| - cdatas = self.chunk_store.getitems(ckeys, on_error="omit") |
| 2025 | + contexts = {} |
| 2026 | + if not isinstance(self._meta_array, np.ndarray): |
| 2027 | + contexts = ConstantMap(ckeys, constant=Context(meta_array=self._meta_array)) |
| 2028 | + cdatas = self.chunk_store.getitems(ckeys, contexts=contexts) |
| 2029 | + |
2066 | 2030 | for ckey, chunk_select, out_select in zip(ckeys, lchunk_selection, lout_selection):
|
2067 | 2031 | if ckey in cdatas:
|
2068 | 2032 | self._process_chunk(
|
|
0 commit comments