from contextlib import contextmanager, ExitStack from pathlib import Path from typing import Iterator, Union, Optional import shutil @contextmanager def ensure_dir(dirpath: Path) -> Iterator[Path]: """ Ensure that the given directory (and any parents) exist. When the context exists, removes any directories that were created. """ dirpath = dirpath.absolute() if dirpath.exists(): assert dirpath.is_dir(), f'Directory {dirpath} is a non-directory file' yield dirpath return # Create the directory and clean it up when we are done with ensure_dir(dirpath.parent): dirpath.mkdir() try: yield dirpath finally: shutil.rmtree(dirpath) @contextmanager def auto_delete(fpath: Path) -> Iterator[Path]: try: yield fpath finally: if fpath.exists(): fpath.unlink() @contextmanager def set_contents(fpath: Path, content: bytes) -> Iterator[Path]: prev_content: Optional[bytes] = None if fpath.exists(): assert fpath.is_file(), 'File {fpath} exists and is not a regular file' prev_content = fpath.read_bytes() with ensure_dir(fpath.parent): fpath.write_bytes(content) try: yield fpath finally: if prev_content is None: fpath.unlink() else: fpath.write_bytes(prev_content)