Skip to content

Commit fe4e11e

Browse files
committed
fix(ZarrAvgMerger): use separate tmpdir attrs to prevent value_store tmpdir from being garbage collected
Previously, when both `value_store` and `count_store` were None, `self.tmpdir` was assigned twice sequentially — causing the first TemporaryDirectory (for value_store) to be immediately garbage collected when the second one (for count_store) overwrote the reference. This silently deleted the value store's backing directory. Fix: introduce `self.value_tmpdir` and `self.count_tmpdir` as separate attributes so both temporary directories remain alive for the lifetime of the ZarrAvgMerger instance. Fixes #8476
1 parent cc92126 commit fe4e11e

1 file changed

Lines changed: 16 additions & 37 deletions

File tree

monai/inferers/merger.py

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -286,32 +286,33 @@ def __init__(
286286
self.value_dtype = value_dtype
287287
self.count_dtype = count_dtype
288288
self.store = store
289-
self.tmpdir: TemporaryDirectory | None
289+
# Use separate tmpdir attributes so both TemporaryDirectory objects remain alive
290+
# for the full lifetime of this instance. Previously a single `self.tmpdir` was
291+
# overwritten when both value_store and count_store were None, causing the first
292+
# TemporaryDirectory to be garbage-collected immediately.
293+
self.value_tmpdir: TemporaryDirectory | None = None
294+
self.count_tmpdir: TemporaryDirectory | None = None
290295

291296
# Handle zarr v3 vs older versions
292297
is_zarr_v3 = version_geq(get_package_version("zarr"), "3.0.0")
293298

294299
if is_zarr_v3:
295300
if value_store is None:
296-
self.tmpdir = TemporaryDirectory()
297-
self.value_store = zarr.storage.LocalStore(self.tmpdir.name) # type: ignore
301+
self.value_tmpdir = TemporaryDirectory()
302+
self.value_store = zarr.storage.LocalStore(self.value_tmpdir.name) # type: ignore
298303
else:
299304
self.value_store = value_store # type: ignore
300305
if count_store is None:
301-
self.tmpdir = TemporaryDirectory()
302-
self.count_store = zarr.storage.LocalStore(self.tmpdir.name) # type: ignore
306+
self.count_tmpdir = TemporaryDirectory()
307+
self.count_store = zarr.storage.LocalStore(self.count_tmpdir.name) # type: ignore
303308
else:
304309
self.count_store = count_store # type: ignore
305310
else:
306-
self.tmpdir = None
307311
self.value_store = zarr.storage.TempStore() if value_store is None else value_store # type: ignore
308312
self.count_store = zarr.storage.TempStore() if count_store is None else count_store # type: ignore
309313

310314
self.chunks = chunks
311315

312-
# Handle compressor/codecs based on zarr version
313-
is_zarr_v3 = version_geq(get_package_version("zarr"), "3.0.0")
314-
315316
# Initialize codecs/compressor attributes with proper types
316317
self.codecs: list | None = None
317318
self.value_codecs: list | None = None
@@ -322,50 +323,28 @@ def __init__(
322323
if codecs is not None:
323324
self.codecs = codecs
324325
elif compressor is not None:
325-
# Convert compressor to codec format
326-
if isinstance(compressor, (list, tuple)):
327-
self.codecs = compressor
328-
else:
329-
self.codecs = [compressor]
326+
self.codecs = compressor if isinstance(compressor, (list, tuple)) else [compressor]
330327
else:
331328
self.codecs = None
332329

333330
if value_codecs is not None:
334331
self.value_codecs = value_codecs
335332
elif value_compressor is not None:
336-
if isinstance(value_compressor, (list, tuple)):
337-
self.value_codecs = value_compressor
338-
else:
339-
self.value_codecs = [value_compressor]
333+
self.value_codecs = value_compressor if isinstance(value_compressor, (list, tuple)) else [value_compressor]
340334
else:
341335
self.value_codecs = None
342336

343337
if count_codecs is not None:
344338
self.count_codecs = count_codecs
345339
elif count_compressor is not None:
346-
if isinstance(count_compressor, (list, tuple)):
347-
self.count_codecs = count_compressor
348-
else:
349-
self.count_codecs = [count_compressor]
340+
self.count_codecs = count_compressor if isinstance(count_compressor, (list, tuple)) else [count_compressor]
350341
else:
351342
self.count_codecs = None
352343
else:
353344
# For zarr v2, use compressors
354-
if codecs is not None:
355-
# If codecs are specified in v2, use the first codec as compressor
356-
self.codecs = codecs[0] if isinstance(codecs, (list, tuple)) else codecs
357-
else:
358-
self.codecs = compressor # type: ignore[assignment]
359-
360-
if value_codecs is not None:
361-
self.value_codecs = value_codecs[0] if isinstance(value_codecs, (list, tuple)) else value_codecs
362-
else:
363-
self.value_codecs = value_compressor # type: ignore[assignment]
364-
365-
if count_codecs is not None:
366-
self.count_codecs = count_codecs[0] if isinstance(count_codecs, (list, tuple)) else count_codecs
367-
else:
368-
self.count_codecs = count_compressor # type: ignore[assignment]
345+
self.codecs = codecs[0] if isinstance(codecs, (list, tuple)) else codecs if codecs is not None else compressor # type: ignore[assignment]
346+
self.value_codecs = value_codecs[0] if isinstance(value_codecs, (list, tuple)) else value_codecs if value_codecs is not None else value_compressor # type: ignore[assignment]
347+
self.count_codecs = count_codecs[0] if isinstance(count_codecs, (list, tuple)) else count_codecs if count_codecs is not None else count_compressor # type: ignore[assignment]
369348

370349
# Create zarr arrays with appropriate parameters based on version
371350
if is_zarr_v3:

0 commit comments

Comments
 (0)