From bed3f8a3cc8db27d6f2e5c59ff64ffd6b9b4e73f Mon Sep 17 00:00:00 2001 From: "Michael A. Davis" <6325127+mrmichaeladavis@users.noreply.github.com> Date: Sun, 31 May 2020 08:14:01 -0500 Subject: [PATCH] Fixed bug with NamedTemporaryFile on Windows (#4) --- xtarfile/zstd.py | 59 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/xtarfile/zstd.py b/xtarfile/zstd.py index cfc90a1..7d2ef34 100644 --- a/xtarfile/zstd.py +++ b/xtarfile/zstd.py @@ -1,6 +1,7 @@ from contextlib import contextmanager from tarfile import open as tarfile_open from tempfile import NamedTemporaryFile +from os import remove as os_remove try: import zstandard @@ -14,29 +15,47 @@ def __init__(self, **kwargs): @contextmanager def read(self, path: str, mode: str): - with NamedTemporaryFile() as decompressed: - with open(path, 'rb') as compressed: - zstd = zstandard.ZstdDecompressor(**self.zstd_kwargs) - zstd.copy_stream(compressed, decompressed) - decompressed.seek(0) - archive = tarfile_open(mode=mode, fileobj=decompressed) - try: - yield archive - finally: - archive.close() + try: + with NamedTemporaryFile(delete=False) as decompressed: + with open(path, 'rb') as compressed: + zstd = zstandard.ZstdDecompressor(**self.zstd_kwargs) + zstd.copy_stream(compressed, decompressed) + decompressed.seek(0) + archive = tarfile_open(mode=mode, fileobj=decompressed) + try: + yield archive + finally: + archive.close() + finally: + # We delete it manually because otherwise on Windows + # it gets deleted before we move it to the output file location. + # This is because on Windows, file handles with the O_TEMPORARY + # flag (which is set if we pass `delete=True`) are deleted as + # soon as they're closed. + decompressed.close() + os_remove(decompressed.name) @contextmanager def write(self, path: str, mode: str): - with NamedTemporaryFile() as decompressed: - archive = tarfile_open(decompressed.name, mode=mode) - try: - yield archive - finally: - archive.close() - decompressed.seek(0) - with open(path, 'wb') as compressed: - zstd = zstandard.ZstdCompressor(**self.zstd_kwargs) - zstd.copy_stream(decompressed, compressed) + try: + with NamedTemporaryFile(delete=False) as decompressed: + archive = tarfile_open(decompressed.name, mode=mode) + try: + yield archive + finally: + archive.close() + decompressed.seek(0) + with open(path, 'wb') as compressed: + zstd = zstandard.ZstdCompressor(**self.zstd_kwargs) + zstd.copy_stream(decompressed, compressed) + finally: + # We delete it manually because otherwise on Windows + # it gets deleted before we move it to the output file location. + # This is because on Windows, file handles with the O_TEMPORARY + # flag (which is set if we pass `delete=True`) are deleted as + # soon as they're closed. + decompressed.close() + os_remove(decompressed.name) if zstandard is None: