diff --git a/zarr/core.py b/zarr/core.py index b7f416d3f8..4c48021b63 100644 --- a/zarr/core.py +++ b/zarr/core.py @@ -265,7 +265,7 @@ def ndim(self): @property def _size(self): - return reduce(operator.mul, self._shape) + return reduce(operator.mul, self._shape, 1) @property def size(self): @@ -322,7 +322,7 @@ def cdata_shape(self): @property def _nchunks(self): - return reduce(operator.mul, self._cdata_shape) + return reduce(operator.mul, self._cdata_shape, 1) @property def nchunks(self): @@ -764,7 +764,11 @@ def _chunk_setitem_nosync(self, cidx, item, value): self._chunk_store[ckey] = cdata def _chunk_key(self, cidx): - return self._key_prefix + '.'.join(map(str, cidx)) + # Empty keys don't play well with file-systems... + if len(cidx) == 0: + return self._key_prefix + '0' + else: + return self._key_prefix + '.'.join(map(str, cidx)) def _decode_chunk(self, cdata): diff --git a/zarr/tests/test_creation.py b/zarr/tests/test_creation.py index bb617fff14..485c17b7de 100644 --- a/zarr/tests/test_creation.py +++ b/zarr/tests/test_creation.py @@ -384,6 +384,37 @@ def test_create(): create(100, compression=1) +def test_create_zero_len(): + + # Just test defaults. + z = create(0) + assert_is_instance(z, Array) + eq((0,), z.shape) + + n = z[:] + eq(0, len(n)) + + +def test_create_no_dims(): + ar = np.ndarray(()) + ar[()] = 100 + z = array(ar) + assert_array_equal(ar, z[:]) + + +def test_create_no_dims_dirstore(): + ar = np.ndarray(()) + ar[()] = 100 + + path = tempfile.mkdtemp() + try: + store = DirectoryStore(path) + z = array(ar, store=store) + assert_array_equal(ar, z[:]) + finally: + shutil.rmtree(path) + + def test_compression_args(): z = create(100, compression='zlib', compression_opts=9) diff --git a/zarr/tests/test_util.py b/zarr/tests/test_util.py index fe4d7aaf05..69e6936cb3 100644 --- a/zarr/tests/test_util.py +++ b/zarr/tests/test_util.py @@ -183,12 +183,15 @@ def test_guess_chunks(): (1000, 10000000, 2), (10000, 10000, 10000), (100000, 100000, 100000), + (0,), + (0, 0), + (1, 2, 0, 4, 5), ) for shape in shapes: chunks = guess_chunks(shape, 1) assert_is_instance(chunks, tuple) eq(len(chunks), len(shape)) - assert all([c <= s for c, s in zip(chunks, shape)]) + assert all([c <= max(s, 1) for c, s in zip(chunks, shape)]) # ludicrous itemsize chunks = guess_chunks((1000000,), 40000000) diff --git a/zarr/util.py b/zarr/util.py index 8419d06ae2..f14303e4fa 100644 --- a/zarr/util.py +++ b/zarr/util.py @@ -40,7 +40,7 @@ def guess_chunks(shape, typesize): """ ndims = len(shape) - chunks = np.array(shape, dtype='=f8') + chunks = np.maximum(np.array(shape, dtype='=f8'), 1) # Determine the optimal chunk size in bytes using a PyTables expression. # This is kept as a float. @@ -153,6 +153,11 @@ def normalize_axis_selection(item, l): stop = l + stop if start < 0 or stop < 0: raise IndexError('index out of bounds: %s, %s' % (start, stop)) + + # Handle zero-length axis. + if start == stop == l == 0: + return slice(0, 0) + if start >= l: raise IndexError('index out of bounds: %s, %s' % (start, stop)) if stop > l: