Skip to content

Commit 76898d0

Browse files
committed
Add key normalization option
1 parent 89659b2 commit 76898d0

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

zarr/n5.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class N5Store(NestedDirectoryStore):
3737
----------
3838
path : string
3939
Location of directory to use as the root of the storage hierarchy.
40+
normalize_keys : bool, optional
41+
If True, all store keys will be normalized to use lower case characters
42+
(e.g. 'foo' and 'FOO' will be treated as equivalent). This can be
43+
useful to avoid potential discrepancies between case-senstive and
44+
case-insensitive file system. Default value is False.
4045
4146
Examples
4247
--------

zarr/storage.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,11 @@ class DirectoryStore(MutableMapping):
673673
----------
674674
path : string
675675
Location of directory to use as the root of the storage hierarchy.
676+
normalize_keys : bool, optional
677+
If True, all store keys will be normalized to use lower case characters
678+
(e.g. 'foo' and 'FOO' will be treated as equivalent). This can be
679+
useful to avoid potential discrepancies between case-senstive and
680+
case-insensitive file system. Default value is False.
676681
677682
Examples
678683
--------
@@ -719,16 +724,21 @@ class DirectoryStore(MutableMapping):
719724
720725
"""
721726

722-
def __init__(self, path):
727+
def __init__(self, path, normalize_keys=False):
723728

724729
# guard conditions
725730
path = os.path.abspath(path)
726731
if os.path.exists(path) and not os.path.isdir(path):
727732
err_fspath_exists_notdir(path)
728733

729734
self.path = path
735+
self.normalize_keys = normalize_keys
736+
737+
def _normalize_key(self, key):
738+
return key.lower() if self.normalize_keys else key
730739

731740
def __getitem__(self, key):
741+
key = self._normalize_key(key)
732742
filepath = os.path.join(self.path, key)
733743
if os.path.isfile(filepath):
734744
with open(filepath, 'rb') as f:
@@ -737,6 +747,7 @@ def __getitem__(self, key):
737747
raise KeyError(key)
738748

739749
def __setitem__(self, key, value):
750+
key = self._normalize_key(key)
740751

741752
# coerce to flat, contiguous array (ideally without copying)
742753
value = ensure_contiguous_ndarray(value)
@@ -777,6 +788,7 @@ def __setitem__(self, key, value):
777788
os.remove(temp_path)
778789

779790
def __delitem__(self, key):
791+
key = self._normalize_key(key)
780792
path = os.path.join(self.path, key)
781793
if os.path.isfile(path):
782794
os.remove(path)
@@ -788,6 +800,7 @@ def __delitem__(self, key):
788800
raise KeyError(key)
789801

790802
def __contains__(self, key):
803+
key = self._normalize_key(key)
791804
file_path = os.path.join(self.path, key)
792805
return os.path.isfile(file_path)
793806

@@ -902,14 +915,19 @@ class TempStore(DirectoryStore):
902915
Prefix for the temporary directory name.
903916
dir : string, optional
904917
Path to parent directory in which to create temporary directory.
918+
normalize_keys : bool, optional
919+
If True, all store keys will be normalized to use lower case characters
920+
(e.g. 'foo' and 'FOO' will be treated as equivalent). This can be
921+
useful to avoid potential discrepancies between case-senstive and
922+
case-insensitive file system. Default value is False.
905923
906924
"""
907925

908926
# noinspection PyShadowingBuiltins
909-
def __init__(self, suffix='', prefix='zarr', dir=None):
927+
def __init__(self, suffix='', prefix='zarr', dir=None, normalize_keys=False):
910928
path = tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=dir)
911929
atexit.register(atexit_rmtree, path)
912-
super(TempStore, self).__init__(path)
930+
super(TempStore, self).__init__(path, normalize_keys=normalize_keys)
913931

914932

915933
_prog_ckey = re.compile(r'^(\d+)(\.\d+)+$')
@@ -936,6 +954,11 @@ class NestedDirectoryStore(DirectoryStore):
936954
----------
937955
path : string
938956
Location of directory to use as the root of the storage hierarchy.
957+
normalize_keys : bool, optional
958+
If True, all store keys will be normalized to use lower case characters
959+
(e.g. 'foo' and 'FOO' will be treated as equivalent). This can be
960+
useful to avoid potential discrepancies between case-senstive and
961+
case-insensitive file system. Default value is False.
939962
940963
Examples
941964
--------
@@ -992,8 +1015,8 @@ class NestedDirectoryStore(DirectoryStore):
9921015
9931016
"""
9941017

995-
def __init__(self, path):
996-
super(NestedDirectoryStore, self).__init__(path)
1018+
def __init__(self, path, normalize_keys=False):
1019+
super(NestedDirectoryStore, self).__init__(path, normalize_keys=normalize_keys)
9971020

9981021
def __getitem__(self, key):
9991022
key = _nested_map_ckey(key)

zarr/tests/test_storage.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -714,10 +714,10 @@ def test_deprecated(self):
714714

715715
class TestDirectoryStore(StoreTests, unittest.TestCase):
716716

717-
def create_store(self):
717+
def create_store(self, normalize_keys=False):
718718
path = tempfile.mkdtemp()
719719
atexit.register(atexit_rmtree, path)
720-
store = DirectoryStore(path)
720+
store = DirectoryStore(path, normalize_keys=normalize_keys)
721721
return store
722722

723723
def test_filesystem_path(self):
@@ -753,13 +753,19 @@ def test_setdel(self):
753753
store = self.create_store()
754754
setdel_hierarchy_checks(store)
755755

756+
def test_normalize_keys(self):
757+
store = self.create_store(normalize_keys=True)
758+
store['FOO'] = b'bar'
759+
assert 'FOO' in store
760+
assert 'foo' in store
761+
756762

757763
class TestNestedDirectoryStore(TestDirectoryStore, unittest.TestCase):
758764

759-
def create_store(self):
765+
def create_store(self, normalize_keys=False):
760766
path = tempfile.mkdtemp()
761767
atexit.register(atexit_rmtree, path)
762-
store = NestedDirectoryStore(path)
768+
store = NestedDirectoryStore(path, normalize_keys=normalize_keys)
763769
return store
764770

765771
def test_chunk_nesting(self):
@@ -777,10 +783,10 @@ def test_chunk_nesting(self):
777783

778784
class TestN5Store(TestNestedDirectoryStore, unittest.TestCase):
779785

780-
def create_store(self):
786+
def create_store(self, normalize_keys=False):
781787
path = tempfile.mkdtemp(suffix='.n5')
782788
atexit.register(atexit_rmtree, path)
783-
store = N5Store(path)
789+
store = N5Store(path, normalize_keys=normalize_keys)
784790
return store
785791

786792
def test_equal(self):

0 commit comments

Comments
 (0)