Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions ink-detection/scripts/prepare_9um_isotropic_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from concurrent.futures import ThreadPoolExecutor
import math
from pathlib import Path
import time

import numpy as np
from numcodecs import Blosc
Expand Down Expand Up @@ -42,6 +43,39 @@ def open_source_array(path: str, level: str) -> zarr.Array:
return node[level]



def publish_partial(staged: Path, output: Path, *, attempts: int = 6, delay: float = 0.5) -> None:
"""Rename a finished staging directory onto the output path.

POSIX renames a directory whose files are open. Windows refuses with
WinError 5 while any file inside is still open, and one handle is enough --
measured on this failure: renaming succeeds with nothing held, and fails with
exactly WinError 5 with a single file inside opened for read. After writing a
multi-gigabyte zarr, something holding a handle for a moment (an indexer, a
scanner) is ordinary, so retry briefly before giving up.

If it still fails, say what is true: every tile was written, so nothing needs
recomputing, and a manual rename finishes the job. The bare traceback this
replaces looked like a lost conversion.
"""
for attempt in range(attempts):
try:
staged.replace(output)
return
except PermissionError as exc:
if attempt + 1 == attempts:
raise SystemExit(
f"The conversion finished -- every tile was written -- but the result "
f"could not be published:\n"
f" {staged}\n"
f" -> {output}\n"
f" {exc}\n"
f"On Windows a directory cannot be renamed while any file inside it is "
f"open. Nothing needs recomputing: rename the staging directory by hand."
) from None
time.sleep(delay * (2 ** attempt))


def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input_zarr", help="Surface-volume OME-Zarr group path or URL (or a bare 3D array).")
Expand Down Expand Up @@ -104,8 +138,10 @@ def process(tile: tuple[int, int, int, int]) -> int:
completed += count
if completed % 50 == 0 or completed == len(tiles):
print(f"tiles={completed}/{len(tiles)}", flush=True)
partial.replace(args.output_zarr)
print(f"wrote {args.output_zarr} shape={tuple(target.shape)}")
output_shape = tuple(target.shape)
del target, group # release the store's handles before renaming its directory
publish_partial(partial, args.output_zarr)
print(f"wrote {args.output_zarr} shape={output_shape}")
return 0


Expand Down
Loading