Skip to content

Commit b87fb5f

Browse files
committed
Move download.py functions to FetcherInterface
Make download_file and download_bytes functions part of the FetcherInterface class. Remove download.py module. Signed-off-by: Teodora Sechkova <[email protected]>
1 parent aac56a2 commit b87fb5f

File tree

3 files changed

+62
-75
lines changed

3 files changed

+62
-75
lines changed

tuf/ngclient/_internal/download.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

tuf/ngclient/fetcher.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@
66

77
# Imports
88
import abc
9-
from typing import Iterator
9+
import logging
10+
import tempfile
11+
from contextlib import contextmanager
12+
from typing import IO, Iterator
13+
from urllib import parse
14+
15+
from tuf import exceptions
16+
17+
logger = logging.getLogger(__name__)
1018

1119

1220
# Classes
@@ -40,3 +48,51 @@ def fetch(self, url: str, required_length: int) -> Iterator[bytes]:
4048
A bytes iterator
4149
"""
4250
raise NotImplementedError # pragma: no cover
51+
52+
@contextmanager
53+
def download_file(self, url: str, required_length: int) -> Iterator[IO]:
54+
"""Opens a connection to 'url' and downloads the content
55+
up to 'required_length'.
56+
57+
Args:
58+
url: a URL string that represents the location of the file.
59+
required_length: an integer value representing the length of
60+
the file or an upper boundary.
61+
62+
Raises:
63+
DownloadLengthMismatchError: a mismatch of observed vs expected
64+
lengths while downloading the file.
65+
66+
Yields:
67+
A file object that points to the contents of 'url'.
68+
"""
69+
# 'url.replace('\\', '/')' is needed for compatibility with
70+
# Windows-based systems, because they might use back-slashes in place
71+
# of forward-slashes. This converts it to the common format.
72+
# unquote() replaces %xx escapes in a url with their single-character
73+
# equivalent. A back-slash may beencoded as %5c in the url, which
74+
# should also be replaced with a forward slash.
75+
url = parse.unquote(url).replace("\\", "/")
76+
logger.debug("Downloading: %s", url)
77+
78+
number_of_bytes_received = 0
79+
80+
with tempfile.TemporaryFile() as temp_file:
81+
chunks = self.fetch(url, required_length)
82+
for chunk in chunks:
83+
temp_file.write(chunk)
84+
number_of_bytes_received += len(chunk)
85+
if number_of_bytes_received > required_length:
86+
raise exceptions.DownloadLengthMismatchError(
87+
required_length, number_of_bytes_received
88+
)
89+
temp_file.seek(0)
90+
yield temp_file
91+
92+
def download_bytes(self, url: str, required_length: int) -> bytes:
93+
"""Download bytes from given url
94+
95+
Returns the downloaded bytes, otherwise like download_file()
96+
"""
97+
with self.download_file(url, required_length) as dl_file:
98+
return dl_file.read()

tuf/ngclient/updater.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from securesystemslib import util as sslib_util
1616

1717
from tuf import exceptions
18-
from tuf.ngclient._internal import download, metadata_bundle, requests_fetcher
18+
from tuf.ngclient._internal import metadata_bundle, requests_fetcher
1919
from tuf.ngclient.fetcher import FetcherInterface
2020

2121
# Globals
@@ -31,7 +31,7 @@
3131
# Classes
3232
class Updater:
3333
"""
34-
An implemetation of the TUF client workflow.
34+
An implementation of the TUF client workflow.
3535
Provides a public API for integration in client applications.
3636
"""
3737

@@ -200,8 +200,8 @@ def download_target(
200200

201201
full_url = parse.urljoin(target_base_url, targetinfo["filepath"])
202202

203-
with download.download_file(
204-
full_url, targetinfo["fileinfo"].length, self._fetcher
203+
with self._fetcher.download_file(
204+
full_url, targetinfo["fileinfo"].length
205205
) as target_file:
206206
_check_file_length(target_file, targetinfo["fileinfo"].length)
207207
_check_hashes_obj(target_file, targetinfo["fileinfo"].hashes)
@@ -220,7 +220,7 @@ def _download_metadata(
220220
else:
221221
filename = f"{version}.{rolename}.json"
222222
url = parse.urljoin(self._metadata_base_url, filename)
223-
return download.download_bytes(url, length, self._fetcher)
223+
return self._fetcher.download_bytes(url, length)
224224

225225
def _load_local_metadata(self, rolename: str) -> bytes:
226226
with open(os.path.join(self._dir, f"{rolename}.json"), "rb") as f:

0 commit comments

Comments
 (0)