diff --git a/src/nd2/nd2file.py b/src/nd2/nd2file.py index 3f925bb..c432aac 100644 --- a/src/nd2/nd2file.py +++ b/src/nd2/nd2file.py @@ -1,5 +1,6 @@ from __future__ import annotations +import inspect import threading import warnings from itertools import product @@ -189,11 +190,19 @@ def __del__(self) -> None: """Delete file handle on garbage collection.""" # if it came in as an open file handle, it's ok to remain open after deletion if not getattr(self, "closed", True) and not self._rdr._was_open: - warnings.warn( - "ND2File file not closed before garbage collection. " - "Please use `with ND2File(...):` context or call `.close()`.", - stacklevel=2, - ) + # this stack inspection is a hack to avoid an unnecessary warning/closure. + # when using the to_dask() method, calling dask map_blocks will greedily + # pickle/unpickle the object. + # that will trigger a call to __getstate__ which calls del state["_rdr"] + # which results in a call to __del__. We avoid that by checking this + # special case here... I'm not sure it's the correct approach, but it works. + stack = inspect.stack() + if len(stack) < 2 or stack[1].function != "_normalize_pickle": + warnings.warn( + "ND2File file not closed before garbage collection. " + "Please use `with ND2File(...):` context or call `.close()`.", + stacklevel=2, + ) self._rdr.close() def __exit__(self, *_: Any) -> None: @@ -215,7 +224,6 @@ def __setstate__(self, d: dict[str, Any]) -> None: self.__dict__ = d self._lock = threading.RLock() self._rdr = ND2Reader.create(self._path, self._error_radius) - if _was_closed: self._rdr.close()