|
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