138
138
139
139
140
140
# Array and AsyncArray are defined in the base ``zarr`` namespace
141
- __all__ = ["create_codec_pipeline" , "parse_array_metadata" ]
141
+ __all__ = [
142
+ "DEFAULT_FILL_VALUE" ,
143
+ "DefaultFillValue" ,
144
+ "create_codec_pipeline" ,
145
+ "parse_array_metadata" ,
146
+ ]
142
147
143
148
logger = getLogger (__name__ )
144
149
145
150
151
+ class DefaultFillValue :
152
+ """
153
+ Sentinel class to indicate that the default fill value should be used.
154
+
155
+ This class exists because conventional values used to convey "defaultness" like ``None`` or
156
+ ``"auto"` are ambiguous when specifying the fill value parameter of a Zarr array.
157
+ The value ``None`` is ambiguous because it is a valid fill value for Zarr V2
158
+ (resulting in ``"fill_value": null`` in array metadata).
159
+ A string like ``"auto"`` is ambiguous because such a string is a valid fill value for an array
160
+ with a string data type.
161
+ An instance of this class lies outside the space of valid fill values, which means it can
162
+ umambiguously express that the default fill value should be used.
163
+ """
164
+
165
+
166
+ DEFAULT_FILL_VALUE = DefaultFillValue ()
167
+
168
+
146
169
def parse_array_metadata (data : Any ) -> ArrayMetadata :
147
170
if isinstance (data , ArrayMetadata ):
148
171
return data
@@ -296,7 +319,7 @@ async def create(
296
319
shape : ShapeLike ,
297
320
dtype : ZDTypeLike ,
298
321
zarr_format : Literal [2 ],
299
- fill_value : Any | None = None ,
322
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
300
323
attributes : dict [str , JSON ] | None = None ,
301
324
chunks : ShapeLike | None = None ,
302
325
dimension_separator : Literal ["." , "/" ] | None = None ,
@@ -320,7 +343,7 @@ async def create(
320
343
shape : ShapeLike ,
321
344
dtype : ZDTypeLike ,
322
345
zarr_format : Literal [3 ],
323
- fill_value : Any | None = None ,
346
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
324
347
attributes : dict [str , JSON ] | None = None ,
325
348
# v3 only
326
349
chunk_shape : ShapeLike | None = None ,
@@ -348,7 +371,7 @@ async def create(
348
371
shape : ShapeLike ,
349
372
dtype : ZDTypeLike ,
350
373
zarr_format : Literal [3 ] = 3 ,
351
- fill_value : Any | None = None ,
374
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
352
375
attributes : dict [str , JSON ] | None = None ,
353
376
# v3 only
354
377
chunk_shape : ShapeLike | None = None ,
@@ -376,7 +399,7 @@ async def create(
376
399
shape : ShapeLike ,
377
400
dtype : ZDTypeLike ,
378
401
zarr_format : ZarrFormat ,
379
- fill_value : Any | None = None ,
402
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
380
403
attributes : dict [str , JSON ] | None = None ,
381
404
# v3 only
382
405
chunk_shape : ShapeLike | None = None ,
@@ -411,7 +434,7 @@ async def create(
411
434
shape : ShapeLike ,
412
435
dtype : ZDTypeLike ,
413
436
zarr_format : ZarrFormat = 3 ,
414
- fill_value : Any | None = None ,
437
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
415
438
attributes : dict [str , JSON ] | None = None ,
416
439
# v3 only
417
440
chunk_shape : ShapeLike | None = None ,
@@ -552,7 +575,7 @@ async def _create(
552
575
shape : ShapeLike ,
553
576
dtype : ZDTypeLike | ZDType [TBaseDType , TBaseScalar ],
554
577
zarr_format : ZarrFormat = 3 ,
555
- fill_value : Any | None = None ,
578
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
556
579
attributes : dict [str , JSON ] | None = None ,
557
580
# v3 only
558
581
chunk_shape : ShapeLike | None = None ,
@@ -673,7 +696,7 @@ def _create_metadata_v3(
673
696
shape : ShapeLike ,
674
697
dtype : ZDType [TBaseDType , TBaseScalar ],
675
698
chunk_shape : ChunkCoords ,
676
- fill_value : Any | None = None ,
699
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
677
700
chunk_key_encoding : ChunkKeyEncodingLike | None = None ,
678
701
codecs : Iterable [Codec | dict [str , JSON ]] | None = None ,
679
702
dimension_names : DimensionNames = None ,
@@ -698,8 +721,9 @@ def _create_metadata_v3(
698
721
else :
699
722
chunk_key_encoding_parsed = chunk_key_encoding
700
723
701
- if fill_value is None :
702
- # v3 spec will not allow a null fill value
724
+ if isinstance (fill_value , DefaultFillValue ) or fill_value is None :
725
+ # Use dtype's default scalar for DefaultFillValue sentinel
726
+ # For v3, None is converted to DefaultFillValue behavior
703
727
fill_value_parsed = dtype .default_scalar ()
704
728
else :
705
729
fill_value_parsed = fill_value
@@ -725,7 +749,7 @@ async def _create_v3(
725
749
dtype : ZDType [TBaseDType , TBaseScalar ],
726
750
chunk_shape : ChunkCoords ,
727
751
config : ArrayConfig ,
728
- fill_value : Any | None = None ,
752
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
729
753
chunk_key_encoding : (
730
754
ChunkKeyEncodingLike
731
755
| tuple [Literal ["default" ], Literal ["." , "/" ]]
@@ -774,22 +798,28 @@ def _create_metadata_v2(
774
798
chunks : ChunkCoords ,
775
799
order : MemoryOrder ,
776
800
dimension_separator : Literal ["." , "/" ] | None = None ,
777
- fill_value : float | None = None ,
801
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
778
802
filters : Iterable [dict [str , JSON ] | numcodecs .abc .Codec ] | None = None ,
779
803
compressor : CompressorLikev2 = None ,
780
804
attributes : dict [str , JSON ] | None = None ,
781
805
) -> ArrayV2Metadata :
782
806
if dimension_separator is None :
783
807
dimension_separator = "."
784
- if fill_value is None :
785
- fill_value = dtype .default_scalar () # type: ignore[assignment]
808
+
809
+ # Handle DefaultFillValue sentinel
810
+ if isinstance (fill_value , DefaultFillValue ):
811
+ fill_value_parsed : Any = dtype .default_scalar ()
812
+ else :
813
+ # For v2, preserve None as-is (backward compatibility)
814
+ fill_value_parsed = fill_value
815
+
786
816
return ArrayV2Metadata (
787
817
shape = shape ,
788
818
dtype = dtype ,
789
819
chunks = chunks ,
790
820
order = order ,
791
821
dimension_separator = dimension_separator ,
792
- fill_value = fill_value ,
822
+ fill_value = fill_value_parsed ,
793
823
compressor = compressor ,
794
824
filters = filters ,
795
825
attributes = attributes ,
@@ -806,7 +836,7 @@ async def _create_v2(
806
836
order : MemoryOrder ,
807
837
config : ArrayConfig ,
808
838
dimension_separator : Literal ["." , "/" ] | None = None ,
809
- fill_value : float | None = None ,
839
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
810
840
filters : Iterable [dict [str , JSON ] | numcodecs .abc .Codec ] | None = None ,
811
841
compressor : CompressorLike = "auto" ,
812
842
attributes : dict [str , JSON ] | None = None ,
@@ -1750,7 +1780,7 @@ def create(
1750
1780
shape : ChunkCoords ,
1751
1781
dtype : ZDTypeLike ,
1752
1782
zarr_format : ZarrFormat = 3 ,
1753
- fill_value : Any | None = None ,
1783
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
1754
1784
attributes : dict [str , JSON ] | None = None ,
1755
1785
# v3 only
1756
1786
chunk_shape : ChunkCoords | None = None ,
@@ -1879,7 +1909,7 @@ def _create(
1879
1909
shape : ChunkCoords ,
1880
1910
dtype : ZDTypeLike ,
1881
1911
zarr_format : ZarrFormat = 3 ,
1882
- fill_value : Any | None = None ,
1912
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
1883
1913
attributes : dict [str , JSON ] | None = None ,
1884
1914
# v3 only
1885
1915
chunk_shape : ChunkCoords | None = None ,
@@ -3836,7 +3866,7 @@ async def from_array(
3836
3866
filters : FiltersLike | Literal ["keep" ] = "keep" ,
3837
3867
compressors : CompressorsLike | Literal ["keep" ] = "keep" ,
3838
3868
serializer : SerializerLike | Literal ["keep" ] = "keep" ,
3839
- fill_value : Any | None = None ,
3869
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
3840
3870
order : MemoryOrder | None = None ,
3841
3871
zarr_format : ZarrFormat | None = None ,
3842
3872
attributes : dict [str , JSON ] | None = None ,
@@ -4098,7 +4128,7 @@ async def init_array(
4098
4128
filters : FiltersLike = "auto" ,
4099
4129
compressors : CompressorsLike = "auto" ,
4100
4130
serializer : SerializerLike = "auto" ,
4101
- fill_value : Any | None = None ,
4131
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
4102
4132
order : MemoryOrder | None = None ,
4103
4133
zarr_format : ZarrFormat | None = 3 ,
4104
4134
attributes : dict [str , JSON ] | None = None ,
@@ -4319,7 +4349,7 @@ async def create_array(
4319
4349
filters : FiltersLike = "auto" ,
4320
4350
compressors : CompressorsLike = "auto" ,
4321
4351
serializer : SerializerLike = "auto" ,
4322
- fill_value : Any | None = None ,
4352
+ fill_value : Any | None = DEFAULT_FILL_VALUE ,
4323
4353
order : MemoryOrder | None = None ,
4324
4354
zarr_format : ZarrFormat | None = 3 ,
4325
4355
attributes : dict [str , JSON ] | None = None ,
0 commit comments