44n5py: The N5 file format implemented in Python
55"""
66
7-
87from __future__ import annotations
98
109from ._version import version as __version__
1817import os
1918import struct
2019import sys
21- from typing import Any , Dict , Optional , cast
2220import warnings
21+ from typing import Any , Dict , Optional , cast
2322
2423import numpy as np
2524from numcodecs .abc import Codec
2625from numcodecs .compat import ndarray_copy
2726from numcodecs .registry import get_codec , register_codec
28-
2927from zarr .meta import ZARR_FORMAT , json_dumps , json_loads
30- from zarr .storage import FSStore , NestedDirectoryStore , _prog_ckey , _prog_number , normalize_storage_path
28+ from zarr .storage import (
29+ FSStore ,
30+ NestedDirectoryStore ,
31+ _prog_ckey ,
32+ _prog_number ,
33+ normalize_storage_path ,
34+ )
3135from zarr .storage import array_meta_key as zarr_array_meta_key
3236from zarr .storage import attrs_key as zarr_attrs_key
3337from zarr .storage import group_meta_key as zarr_group_meta_key
@@ -94,7 +98,9 @@ def __getitem__(self, key: str) -> bytes:
9498 elif key .endswith (zarr_array_meta_key ):
9599 key_new = key .replace (zarr_array_meta_key , n5_attrs_key )
96100 top_level = key == zarr_array_meta_key
97- value = array_metadata_to_zarr (self ._load_n5_attrs (key_new ), top_level = top_level )
101+ value = array_metadata_to_zarr (
102+ self ._load_n5_attrs (key_new ), top_level = top_level
103+ )
98104 return json_dumps (value )
99105
100106 elif key .endswith (zarr_attrs_key ):
@@ -127,7 +133,9 @@ def __setitem__(self, key: str, value: Any):
127133 key_new = key .replace (zarr_array_meta_key , n5_attrs_key )
128134 top_level = key == zarr_array_meta_key
129135 n5_attrs = self ._load_n5_attrs (key_new )
130- n5_attrs .update (** array_metadata_to_n5 (json_loads (value ), top_level = top_level ))
136+ n5_attrs .update (
137+ ** array_metadata_to_n5 (json_loads (value ), top_level = top_level )
138+ )
131139 value = json_dumps (n5_attrs )
132140
133141 elif key .endswith (zarr_attrs_key ):
@@ -138,7 +146,9 @@ def __setitem__(self, key: str, value: Any):
138146
139147 for k in n5_keywords :
140148 if k in zarr_attrs :
141- warnings .warn (f"Attribute { k } is a reserved N5 keyword" , UserWarning )
149+ warnings .warn (
150+ f"Attribute { k } is a reserved N5 keyword" , UserWarning
151+ )
142152
143153 # remove previous user attributes
144154 for k in list (n5_attrs .keys ()):
@@ -272,11 +282,10 @@ def _is_array(self, path: str):
272282 def _contains_attrs (self , path : str ):
273283 if path is None :
274284 attrs_key = n5_attrs_key
285+ elif not path .endswith (n5_attrs_key ):
286+ attrs_key = os .path .join (path , n5_attrs_key )
275287 else :
276- if not path .endswith (n5_attrs_key ):
277- attrs_key = os .path .join (path , n5_attrs_key )
278- else :
279- attrs_key = path
288+ attrs_key = path
280289
281290 attrs = attrs_to_zarr (self ._load_n5_attrs (attrs_key ))
282291 return len (attrs ) > 0
@@ -379,7 +388,9 @@ def __getitem__(self, key: str) -> bytes:
379388 elif key .endswith (zarr_array_meta_key ):
380389 key_new = key .replace (zarr_array_meta_key , self ._array_meta_key )
381390 top_level = key == zarr_array_meta_key
382- value = array_metadata_to_zarr (self ._load_n5_attrs (key_new ), top_level = top_level )
391+ value = array_metadata_to_zarr (
392+ self ._load_n5_attrs (key_new ), top_level = top_level
393+ )
383394 return json_dumps (value )
384395
385396 elif key .endswith (zarr_attrs_key ):
@@ -412,7 +423,9 @@ def __setitem__(self, key: str, value: Any):
412423 key_new = key .replace (zarr_array_meta_key , self ._array_meta_key )
413424 top_level = key == zarr_array_meta_key
414425 n5_attrs = self ._load_n5_attrs (key_new )
415- n5_attrs .update (** array_metadata_to_n5 (json_loads (value ), top_level = top_level ))
426+ n5_attrs .update (
427+ ** array_metadata_to_n5 (json_loads (value ), top_level = top_level )
428+ )
416429
417430 value = json_dumps (n5_attrs )
418431
@@ -424,7 +437,9 @@ def __setitem__(self, key: str, value: Any):
424437
425438 for k in n5_keywords :
426439 if k in zarr_attrs .keys ():
427- warnings .warn (f"Attribute { k } is a reserved N5 keyword" , UserWarning )
440+ warnings .warn (
441+ f"Attribute { k } is a reserved N5 keyword" , UserWarning
442+ )
428443
429444 # replace previous user attributes
430445 for k in list (n5_attrs .keys ()):
@@ -552,11 +567,10 @@ def _is_array(self, path: Optional[str]):
552567 def _contains_attrs (self , path : Optional [str ]):
553568 if path is None :
554569 attrs_key = self ._attrs_key
570+ elif not path .endswith (self ._attrs_key ):
571+ attrs_key = os .path .join (path , self ._attrs_key )
555572 else :
556- if not path .endswith (self ._attrs_key ):
557- attrs_key = os .path .join (path , self ._attrs_key )
558- else :
559- attrs_key = path
573+ attrs_key = path
560574
561575 attrs = attrs_to_zarr (self ._load_n5_attrs (attrs_key ))
562576 return len (attrs ) > 0
@@ -599,7 +613,9 @@ def group_metadata_to_zarr(group_metadata: Dict[str, Any]) -> Dict[str, Any]:
599613 return group_metadata
600614
601615
602- def array_metadata_to_n5 (array_metadata : Dict [str , Any ], top_level = False ) -> Dict [str , Any ]:
616+ def array_metadata_to_n5 (
617+ array_metadata : Dict [str , Any ], top_level = False
618+ ) -> Dict [str , Any ]:
603619 """Convert array metadata from zarr to N5 format. If the `top_level` keyword argument is True,
604620 then the `N5` : N5_FORMAT key : value pair will be inserted into the metadata."""
605621
@@ -611,14 +627,19 @@ def array_metadata_to_n5(array_metadata: Dict[str, Any], top_level=False) -> Dic
611627 try :
612628 dtype = np .dtype (array_metadata ["dataType" ])
613629 except TypeError :
614- raise TypeError (f"Data type { array_metadata ['dataType' ]} is not supported by N5" )
630+ raise TypeError (
631+ f"Data type { array_metadata ['dataType' ]} is not supported by N5"
632+ )
615633
616634 array_metadata ["dataType" ] = dtype .name
617635 array_metadata ["dimensions" ] = array_metadata ["dimensions" ][::- 1 ]
618636 array_metadata ["blockSize" ] = array_metadata ["blockSize" ][::- 1 ]
619637
620638 if "fill_value" in array_metadata :
621- if array_metadata ["fill_value" ] != 0 and array_metadata ["fill_value" ] is not None :
639+ if (
640+ array_metadata ["fill_value" ] != 0
641+ and array_metadata ["fill_value" ] is not None
642+ ):
622643 raise ValueError (
623644 f"""Received fill_value = { array_metadata ['fill_value' ]} ,
624645 but N5 only supports fill_value = 0"""
@@ -634,7 +655,9 @@ def array_metadata_to_n5(array_metadata: Dict[str, Any], top_level=False) -> Dic
634655
635656 if "filters" in array_metadata :
636657 if array_metadata ["filters" ] != [] and array_metadata ["filters" ] is not None :
637- raise ValueError ("Received filters, but N5 storage does not support zarr filters" )
658+ raise ValueError (
659+ "Received filters, but N5 storage does not support zarr filters"
660+ )
638661 del array_metadata ["filters" ]
639662
640663 assert "compression" in array_metadata
@@ -691,7 +714,9 @@ def attrs_to_zarr(attrs: Dict[str, Any]) -> Dict[str, Any]:
691714 return attrs
692715
693716
694- def compressor_config_to_n5 (compressor_config : Optional [Dict [str , Any ]]) -> Dict [str , Any ]:
717+ def compressor_config_to_n5 (
718+ compressor_config : Optional [Dict [str , Any ]],
719+ ) -> Dict [str , Any ]:
695720 if compressor_config is None :
696721 return {"type" : "raw" }
697722 else :
@@ -751,7 +776,9 @@ def compressor_config_to_n5(compressor_config: Optional[Dict[str, Any]]) -> Dict
751776 return n5_config
752777
753778
754- def compressor_config_to_zarr (compressor_config : Dict [str , Any ]) -> Optional [Dict [str , Any ]]:
779+ def compressor_config_to_zarr (
780+ compressor_config : Dict [str , Any ],
781+ ) -> Optional [Dict [str , Any ]]:
755782 codec_id = compressor_config ["type" ]
756783 zarr_config = {"id" : codec_id }
757784
@@ -779,7 +806,7 @@ def compressor_config_to_zarr(compressor_config: Dict[str, Any]) -> Optional[Dic
779806 zarr_config ["filters" ] = None
780807
781808 elif codec_id == "gzip" :
782- if "useZlib" in compressor_config and compressor_config [ "useZlib" ] :
809+ if compressor_config . get ( "useZlib" ) :
783810 zarr_config ["id" ] = "zlib"
784811 zarr_config ["level" ] = compressor_config ["level" ]
785812 else :
@@ -808,10 +835,16 @@ def __init__(self, dtype, chunk_shape, compressor_config=None, compressor=None):
808835
809836 if compressor :
810837 if compressor_config is not None :
811- raise ValueError ("Only one of compressor_config or compressor should be given." )
838+ raise ValueError (
839+ "Only one of compressor_config or compressor should be given."
840+ )
812841 compressor_config = compressor .get_config ()
813842
814- if compressor_config is None and compressor is None or compressor_config ["id" ] == "raw" :
843+ if (
844+ compressor_config is None
845+ and compressor is None
846+ or compressor_config ["id" ] == "raw"
847+ ):
815848 self .compressor_config = None
816849 self ._compressor = None
817850 else :
@@ -884,7 +917,8 @@ def _create_header(chunk):
884917 def _read_header (chunk ):
885918 num_dims = struct .unpack (">H" , chunk [2 :4 ])[0 ]
886919 shape = tuple (
887- struct .unpack (">I" , chunk [i : i + 4 ])[0 ] for i in range (4 , num_dims * 4 + 4 , 4 )
920+ struct .unpack (">I" , chunk [i : i + 4 ])[0 ]
921+ for i in range (4 , num_dims * 4 + 4 , 4 )
888922 )[::- 1 ]
889923
890924 len_header = 4 + num_dims * 4
@@ -908,4 +942,4 @@ def _from_big_endian(self, data):
908942 return a .astype (self .dtype )
909943
910944
911- register_codec (N5ChunkWrapper , N5ChunkWrapper .codec_id )
945+ register_codec (N5ChunkWrapper , N5ChunkWrapper .codec_id )
0 commit comments