Skip to content

Commit f360fc6

Browse files
authored
Remove config warning if only one implementation exists (#2571)
* add test_warning_on_missing_codec_config * improve config tests * remove warning if only one implementation exists
1 parent a7714c7 commit f360fc6

File tree

2 files changed

+84
-51
lines changed

2 files changed

+84
-51
lines changed

src/zarr/registry.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ def get_codec_class(key: str, reload_config: bool = False) -> type[Codec]:
138138

139139
config_entry = config.get("codecs", {}).get(key)
140140
if config_entry is None:
141+
if len(codec_classes) == 1:
142+
return next(iter(codec_classes.values()))
141143
warnings.warn(
142144
f"Codec '{key}' not configured in config. Selecting any implementation.", stacklevel=2
143145
)

tests/test_config.py

Lines changed: 82 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class MockEnvCodecPipeline(CodecPipeline):
143143
assert get_pipeline_class(reload_config=True) == MockEnvCodecPipeline
144144

145145

146+
@pytest.mark.filterwarnings("error")
146147
@pytest.mark.parametrize("store", ["local", "memory"], indirect=["store"])
147148
def test_config_codec_implementation(store: Store) -> None:
148149
# has default value
@@ -156,24 +157,29 @@ async def _encode_single(
156157
) -> CodecOutput | None:
157158
_mock.call()
158159

159-
config.set({"codecs.blosc": fully_qualified_name(MockBloscCodec)})
160160
register_codec("blosc", MockBloscCodec)
161-
assert get_codec_class("blosc") == MockBloscCodec
162-
163-
# test if codec is used
164-
arr = Array.create(
165-
store=store,
166-
shape=(100,),
167-
chunks=(10,),
168-
zarr_format=3,
169-
dtype="i4",
170-
codecs=[BytesCodec(), {"name": "blosc", "configuration": {}}],
171-
)
172-
arr[:] = range(100)
173-
_mock.call.assert_called()
161+
with config.set({"codecs.blosc": fully_qualified_name(MockBloscCodec)}):
162+
assert get_codec_class("blosc") == MockBloscCodec
163+
164+
# test if codec is used
165+
arr = Array.create(
166+
store=store,
167+
shape=(100,),
168+
chunks=(10,),
169+
zarr_format=3,
170+
dtype="i4",
171+
codecs=[BytesCodec(), {"name": "blosc", "configuration": {}}],
172+
)
173+
arr[:] = range(100)
174+
_mock.call.assert_called()
175+
176+
# test set codec with environment variable
177+
class NewBloscCodec(BloscCodec):
178+
pass
174179

175-
with mock.patch.dict(os.environ, {"ZARR_CODECS__BLOSC": fully_qualified_name(BloscCodec)}):
176-
assert get_codec_class("blosc", reload_config=True) == BloscCodec
180+
register_codec("blosc", NewBloscCodec)
181+
with mock.patch.dict(os.environ, {"ZARR_CODECS__BLOSC": fully_qualified_name(NewBloscCodec)}):
182+
assert get_codec_class("blosc", reload_config=True) == NewBloscCodec
177183

178184

179185
@pytest.mark.parametrize("store", ["local", "memory"], indirect=["store"])
@@ -183,18 +189,17 @@ def test_config_ndbuffer_implementation(store: Store) -> None:
183189

184190
# set custom ndbuffer with TestNDArrayLike implementation
185191
register_ndbuffer(NDBufferUsingTestNDArrayLike)
186-
config.set({"ndbuffer": fully_qualified_name(NDBufferUsingTestNDArrayLike)})
187-
assert get_ndbuffer_class() == NDBufferUsingTestNDArrayLike
188-
arr = Array.create(
189-
store=store,
190-
shape=(100,),
191-
chunks=(10,),
192-
zarr_format=3,
193-
dtype="i4",
194-
)
195-
got = arr[:]
196-
print(type(got))
197-
assert isinstance(got, TestNDArrayLike)
192+
with config.set({"ndbuffer": fully_qualified_name(NDBufferUsingTestNDArrayLike)}):
193+
assert get_ndbuffer_class() == NDBufferUsingTestNDArrayLike
194+
arr = Array.create(
195+
store=store,
196+
shape=(100,),
197+
chunks=(10,),
198+
zarr_format=3,
199+
dtype="i4",
200+
)
201+
got = arr[:]
202+
assert isinstance(got, TestNDArrayLike)
198203

199204

200205
def test_config_buffer_implementation() -> None:
@@ -208,27 +213,53 @@ def test_config_buffer_implementation() -> None:
208213
arr[:] = np.arange(100)
209214

210215
register_buffer(TestBuffer)
211-
config.set({"buffer": fully_qualified_name(TestBuffer)})
212-
assert get_buffer_class() == TestBuffer
213-
214-
# no error using TestBuffer
215-
data = np.arange(100)
216-
arr[:] = np.arange(100)
217-
assert np.array_equal(arr[:], data)
218-
219-
data2d = np.arange(1000).reshape(100, 10)
220-
arr_sharding = zeros(
221-
shape=(100, 10),
222-
store=StoreExpectingTestBuffer(),
223-
codecs=[ShardingCodec(chunk_shape=(10, 10))],
224-
)
225-
arr_sharding[:] = data2d
226-
assert np.array_equal(arr_sharding[:], data2d)
216+
with config.set({"buffer": fully_qualified_name(TestBuffer)}):
217+
assert get_buffer_class() == TestBuffer
227218

228-
arr_Crc32c = zeros(
229-
shape=(100, 10),
230-
store=StoreExpectingTestBuffer(),
231-
codecs=[BytesCodec(), Crc32cCodec()],
232-
)
233-
arr_Crc32c[:] = data2d
234-
assert np.array_equal(arr_Crc32c[:], data2d)
219+
# no error using TestBuffer
220+
data = np.arange(100)
221+
arr[:] = np.arange(100)
222+
assert np.array_equal(arr[:], data)
223+
224+
data2d = np.arange(1000).reshape(100, 10)
225+
arr_sharding = zeros(
226+
shape=(100, 10),
227+
store=StoreExpectingTestBuffer(),
228+
codecs=[ShardingCodec(chunk_shape=(10, 10))],
229+
)
230+
arr_sharding[:] = data2d
231+
assert np.array_equal(arr_sharding[:], data2d)
232+
233+
arr_Crc32c = zeros(
234+
shape=(100, 10),
235+
store=StoreExpectingTestBuffer(),
236+
codecs=[BytesCodec(), Crc32cCodec()],
237+
)
238+
arr_Crc32c[:] = data2d
239+
assert np.array_equal(arr_Crc32c[:], data2d)
240+
241+
242+
@pytest.mark.filterwarnings("error")
243+
def test_warning_on_missing_codec_config() -> None:
244+
class NewCodec(BytesCodec):
245+
pass
246+
247+
class NewCodec2(BytesCodec):
248+
pass
249+
250+
# error if codec is not registered
251+
with pytest.raises(KeyError):
252+
get_codec_class("missing_codec")
253+
254+
# no warning if only one implementation is available
255+
register_codec("new_codec", NewCodec)
256+
get_codec_class("new_codec")
257+
258+
# warning because multiple implementations are available but none is selected in the config
259+
register_codec("new_codec", NewCodec2)
260+
with pytest.warns(UserWarning):
261+
get_codec_class("new_codec")
262+
263+
# no warning if multiple implementations are available and one is selected in the config
264+
with config.set({"codecs.new_codec": fully_qualified_name(NewCodec)}):
265+
get_codec_class("new_codec")

0 commit comments

Comments
 (0)