Skip to content

Commit 042f690

Browse files
authored
Merge pull request #106 from ImageMarkup/permission-errors-on-exit
Silence permission errors for deleting partially downloaded images
2 parents afd860b + 288e261 commit 042f690

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

isic_cli/cli/image.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import csv
66
import functools
77
import itertools
8+
import logging
89
import os
910
from pathlib import Path
1011
import signal
@@ -33,11 +34,29 @@
3334
from isic_cli.cli.context import IsicContext
3435

3536

37+
logger = logging.getLogger(__name__)
38+
39+
3640
def cleanup_partially_downloaded_files(directory: Path) -> None:
41+
permission_errors = False
3742
for p in directory.glob(f"**/.isic-partial.{os.getpid()}.*"):
3843
# missing_ok=True because it's possible that another thread moved the temporary file to
3944
# its final destination after listing it but before unlinking.
40-
p.unlink(missing_ok=True)
45+
try:
46+
p.unlink(missing_ok=True)
47+
except PermissionError: # noqa: PERF203
48+
# frequently on windows this is raised. it appears like this could be caused by
49+
# antivirus or various indexers that attempt to use the file shortly after it's
50+
# created.
51+
permission_errors = True
52+
53+
if permission_errors:
54+
logger.warning(
55+
click.style(
56+
"Permission error while cleaning up one or more partially downloaded files",
57+
fg="yellow",
58+
)
59+
)
4160

4261

4362
def _check_and_confirm_available_disk_space(outdir: Path, download_size: int) -> None:

tests/test_cli_image.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import logging
34
import os
45
from pathlib import Path
56

@@ -90,6 +91,33 @@ def test_image_download_cleanup(cli_run, outdir):
9091
assert not partial_file.exists()
9192

9293

94+
@pytest.mark.usefixtures("_isolated_filesystem", "_mock_images")
95+
def test_image_download_cleanup_permission_error(cli_run, outdir, mocker, caplog):
96+
partial_file = Path(outdir) / f".isic-partial.{os.getpid()}.ISIC_0000000.jpg"
97+
partial_file.parent.mkdir(parents=True)
98+
partial_file.touch()
99+
100+
original_unlink = Path.unlink
101+
102+
def mock_unlink(self, *, missing_ok=False):
103+
if str(partial_file) == str(self):
104+
raise PermissionError("Access is denied")
105+
return original_unlink(self, missing_ok=missing_ok)
106+
107+
mocker.patch.object(Path, "unlink", mock_unlink)
108+
caplog.set_level(logging.WARNING)
109+
110+
result = cli_run(["image", "download", outdir])
111+
assert result.exit_code == 0
112+
113+
# run manually since atexit won't run in the test environment
114+
cleanup_partially_downloaded_files(Path(outdir))
115+
116+
assert (
117+
"Permission error while cleaning up one or more partially downloaded files" in caplog.text
118+
)
119+
120+
93121
@pytest.mark.usefixtures("_isolated_filesystem", "_mock_images")
94122
def test_image_download_legacy_diagnosis_unsupported(cli_run, outdir):
95123
result = cli_run(["image", "download", outdir, "--search", "diagnosis:melanoma"])

0 commit comments

Comments
 (0)