Skip to content

Commit 4e15fde

Browse files
committed
Allow concurrent call to url_download
When two pytest instances run in parallel and download the same ISO using 'url_download', the resulting file ends up corrupted. This commit uses 'tempfile.NamedTemporaryFile' to avoid writing to the same file. The new implementation also remove the temporary .part file if the download fails. Signed-off-by: Vincent Michel <vincent.michel@vates.tech>
1 parent 3638b63 commit 4e15fde

1 file changed

Lines changed: 26 additions & 7 deletions

File tree

lib/common.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
import random
1111
import string
1212
import sys
13+
import tempfile
1314
import time
1415
import traceback
1516
from datetime import datetime
1617
from enum import Enum
1718
from functools import lru_cache
19+
from pathlib import Path
1820
from uuid import UUID
1921

2022
import requests
@@ -317,13 +319,30 @@ def strtobool(val: str | None) -> bool:
317319
raise ValueError("invalid truth value '{}'".format(val))
318320

319321
def url_download(url: str, filename: str) -> None:
320-
r = requests.get(url, stream=True)
321-
r.raise_for_status()
322-
tempfilename = filename + ".part"
323-
with open(tempfilename, 'wb') as fd:
324-
for chunk in r.iter_content(chunk_size=128):
325-
fd.write(chunk)
326-
os.rename(tempfilename, filename)
322+
"""
323+
Download the content of `url` to the `filename` destination.
324+
325+
A randomized filename is used during download to prevent file corruption on
326+
concurrent use. If the download fails then the temporary file is removed.
327+
"""
328+
destination = Path(filename)
329+
destination.parent.mkdir(parents=True, exist_ok=True)
330+
with requests.get(url, stream=True) as r:
331+
r.raise_for_status()
332+
temp_name: str | None = None
333+
try:
334+
with tempfile.NamedTemporaryFile(
335+
dir=destination.parent, prefix=f"{destination.name}.", suffix=".part", delete=False
336+
) as fd:
337+
temp_name = fd.name
338+
for chunk in r.iter_content(chunk_size=128):
339+
fd.write(chunk)
340+
except BaseException:
341+
if temp_name is not None:
342+
os.unlink(temp_name)
343+
raise
344+
else:
345+
os.rename(temp_name, filename)
327346

328347
def randid(length: int = 6) -> str:
329348
"""

0 commit comments

Comments
 (0)