Skip to content

Commit e2fec99

Browse files
committed
migrate_1to2
1 parent ca2ffc0 commit e2fec99

File tree

4 files changed

+144
-2
lines changed

4 files changed

+144
-2
lines changed

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ setenv =
1212
commands =
1313
python setup.py build_ext --inplace
1414
py27: nosetests -v zarr
15-
py34,py35: nosetests -v --with-coverage --cover-erase --cover-min-percentage=100 --cover-package=zarr --with-doctest --doctest-options=+NORMALIZE_WHITESPACE zarr
15+
py34,py35: nosetests -v --ignore-files=meta_v1.py --with-coverage --cover-erase --cover-min-percentage=100 --cover-package=zarr --with-doctest --doctest-options=+NORMALIZE_WHITESPACE zarr
1616
py34,py35: python -m doctest -o NORMALIZE_WHITESPACE -o ELLIPSIS docs/tutorial.rst docs/spec/v2.rst
1717
py35: flake8 zarr
1818
python setup.py bdist_wheel

zarr/meta_v1.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import, print_function, division
3+
import json
4+
5+
6+
import numpy as np
7+
8+
9+
from zarr.compat import PY2, text_type
10+
from zarr.errors import MetadataError
11+
12+
13+
def decode_metadata(b):
14+
s = text_type(b, 'ascii')
15+
meta = json.loads(s)
16+
zarr_format = meta.get('zarr_format', None)
17+
if zarr_format != 1:
18+
raise MetadataError('unsupported zarr format: %s' % zarr_format)
19+
try:
20+
meta = dict(
21+
zarr_format=meta['zarr_format'],
22+
shape=tuple(meta['shape']),
23+
chunks=tuple(meta['chunks']),
24+
dtype=decode_dtype(meta['dtype']),
25+
compression=meta['compression'],
26+
compression_opts=meta['compression_opts'],
27+
fill_value=meta['fill_value'],
28+
order=meta['order'],
29+
)
30+
except Exception as e:
31+
raise MetadataError('error decoding metadata: %s' % e)
32+
else:
33+
return meta
34+
35+
36+
def encode_metadata(meta):
37+
meta = dict(
38+
zarr_format=1,
39+
shape=meta['shape'],
40+
chunks=meta['chunks'],
41+
dtype=encode_dtype(meta['dtype']),
42+
compression=meta['compression'],
43+
compression_opts=meta['compression_opts'],
44+
fill_value=meta['fill_value'],
45+
order=meta['order'],
46+
)
47+
s = json.dumps(meta, indent=4, sort_keys=True, ensure_ascii=True)
48+
b = s.encode('ascii')
49+
return b
50+
51+
52+
def encode_dtype(d):
53+
if d.fields is None:
54+
return d.str
55+
else:
56+
return d.descr
57+
58+
59+
def _decode_dtype_descr(d):
60+
# need to convert list of lists to list of tuples
61+
if isinstance(d, list):
62+
# recurse to handle nested structures
63+
if PY2: # pragma: no cover
64+
# under PY2 numpy rejects unicode field names
65+
d = [(f.encode('ascii'), _decode_dtype_descr(v))
66+
for f, v in d]
67+
else:
68+
d = [(f, _decode_dtype_descr(v)) for f, v in d]
69+
return d
70+
71+
72+
def decode_dtype(d):
73+
d = _decode_dtype_descr(d)
74+
return np.dtype(d)

zarr/storage.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,3 +769,32 @@ def getsize(self, path=None):
769769
raise ValueError('path not found: %r' % path)
770770
else:
771771
return 0
772+
773+
774+
def migrate_1to2(store):
775+
"""Migrate array metadata in `store` from Zarr format version 1 to
776+
version 2.
777+
778+
Parameters
779+
----------
780+
store : MutableMapping
781+
Store to be migrated.
782+
783+
Notes
784+
-----
785+
Version 1 did not support hierarchies, so this migration function will
786+
look for a single array in `store` and migrate the array metadata to
787+
version 2.
788+
789+
"""
790+
791+
# migrate metadata
792+
from zarr import meta_v1
793+
meta = meta_v1.decode_metadata(store['meta'])
794+
del store['meta']
795+
meta['filters'] = None
796+
store[array_meta_key] = encode_array_metadata(meta)
797+
798+
# migrate user attributes
799+
store[attrs_key] = store['attrs']
800+
del store['attrs']

zarr/tests/test_storage.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616

1717
from zarr.storage import init_array, array_meta_key, attrs_key, DictStore, \
18-
DirectoryStore, ZipStore, init_group, group_meta_key, getsize
18+
DirectoryStore, ZipStore, init_group, group_meta_key, getsize, migrate_1to2
1919
from zarr.meta import decode_array_metadata, encode_array_metadata, \
2020
ZARR_FORMAT, decode_group_metadata, encode_group_metadata
2121
from zarr.compat import text_type
@@ -642,3 +642,42 @@ def test_getsize():
642642
store['baz/quux'] = b'ccccc'
643643
eq(7, getsize(store))
644644
eq(5, getsize(store, 'baz'))
645+
646+
647+
def test_migrate_1to2():
648+
from zarr import meta_v1
649+
650+
# N.B., version 1 did not support hierarchies, so we only have to be
651+
# concerned about migrating a single array at the root of the store
652+
653+
# setup
654+
store = dict()
655+
meta = dict(
656+
shape=(100,),
657+
chunks=(10,),
658+
dtype=np.dtype('f4'),
659+
compression='zlib',
660+
compression_opts=1,
661+
fill_value=None,
662+
order='C'
663+
)
664+
meta_json = meta_v1.encode_metadata(meta)
665+
store['meta'] = meta_json
666+
store['attrs'] = json.dumps(dict()).encode('ascii')
667+
668+
# run migration
669+
migrate_1to2(store)
670+
671+
# check results
672+
assert 'meta' not in store
673+
assert '.zarray' in store
674+
assert 'attrs' not in store
675+
assert '.zattrs' in store
676+
meta_migrated = decode_array_metadata(store['.zarray'])
677+
eq(2, meta_migrated['zarr_format'])
678+
# preserved fields
679+
for f in 'shape', 'chunks', 'dtype', 'compression', 'compression_opts', \
680+
'fill_value', 'order':
681+
eq(meta[f], meta_migrated[f])
682+
# TODO migrate should have added empty filters field
683+
# assert_is_none(meta_migrated['filters'])

0 commit comments

Comments
 (0)