Skip to content

Commit 40f9f2d

Browse files
committed
Add type annotations to download.py and fetcher.py
Run mypy with --install-types and --non-interactive options to install all suggested stub packages. In our case this is the stub for the requests package. Include urllib3 to the ignored imports. Signed-off-by: Teodora Sechkova <tsechkova@vmware.com>
1 parent c67a536 commit 40f9f2d

5 files changed

Lines changed: 36 additions & 21 deletions

File tree

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ files =
2222

2323
[mypy-securesystemslib.*]
2424
ignore_missing_imports = True
25+
26+
[mypy-urllib3.*]
27+
ignore_missing_imports = True

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@ commands =
5151
# work, unfortunately each subdirectory has to be ignored explicitly.
5252
pylint -j 0 tuf --ignore=tuf/api,tuf/api/serialization,tuf/ngclient,tuf/ngclient/_internal
5353

54-
mypy
54+
mypy --install-types --non-interactive
5555

5656
bandit -r tuf

tuf/ngclient/_internal/download.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,24 @@
2626
import logging
2727
import tempfile
2828
import timeit
29+
from typing import IO
2930
from urllib import parse
3031

3132
from securesystemslib import formats as sslib_formats
3233

33-
import tuf
34-
from tuf import exceptions, formats
34+
from tuf import exceptions, formats, settings
35+
from tuf.ngclient import FetcherInterface
3536

3637
# See 'log.py' to learn how logging is handled in TUF.
3738
logger = logging.getLogger(__name__)
3839

3940

40-
def download_file(url, required_length, fetcher, strict_required_length=True):
41+
def download_file(
42+
url: str,
43+
required_length: int,
44+
fetcher: FetcherInterface,
45+
strict_required_length: bool = True,
46+
) -> IO[bytes]:
4147
"""
4248
<Purpose>
4349
Given the url and length of the desired file, this function opens a
@@ -92,7 +98,7 @@ def download_file(url, required_length, fetcher, strict_required_length=True):
9298
# the downloaded file.
9399
temp_file = tempfile.TemporaryFile() # pylint: disable=consider-using-with
94100

95-
average_download_speed = 0
101+
average_download_speed = 0.0
96102
number_of_bytes_received = 0
97103

98104
try:
@@ -110,7 +116,7 @@ def download_file(url, required_length, fetcher, strict_required_length=True):
110116
number_of_bytes_received / seconds_spent_receiving
111117
)
112118

113-
if average_download_speed < tuf.settings.MIN_AVERAGE_DOWNLOAD_SPEED:
119+
if average_download_speed < settings.MIN_AVERAGE_DOWNLOAD_SPEED:
114120
logger.debug(
115121
"The average download speed dropped below the minimum"
116122
" average download speed set in tuf.settings.py."
@@ -142,7 +148,12 @@ def download_file(url, required_length, fetcher, strict_required_length=True):
142148
return temp_file
143149

144150

145-
def download_bytes(url, required_length, fetcher, strict_required_length=True):
151+
def download_bytes(
152+
url: str,
153+
required_length: int,
154+
fetcher: FetcherInterface,
155+
strict_required_length: bool = True,
156+
) -> bytes:
146157
"""Download bytes from given url
147158
148159
Returns the downloaded bytes, otherwise like download_file()
@@ -154,11 +165,11 @@ def download_bytes(url, required_length, fetcher, strict_required_length=True):
154165

155166

156167
def _check_downloaded_length(
157-
total_downloaded,
158-
required_length,
159-
strict_required_length=True,
160-
average_download_speed=None,
161-
):
168+
total_downloaded: int,
169+
required_length: int,
170+
strict_required_length: bool,
171+
average_download_speed: float,
172+
) -> None:
162173
"""
163174
<Purpose>
164175
A helper function which checks whether the total number of downloaded
@@ -216,7 +227,7 @@ def _check_downloaded_length(
216227

217228
# If the average download speed is below a certain threshold, we
218229
# flag this as a possible slow-retrieval attack.
219-
if average_download_speed < tuf.settings.MIN_AVERAGE_DOWNLOAD_SPEED:
230+
if average_download_speed < settings.MIN_AVERAGE_DOWNLOAD_SPEED:
220231
raise exceptions.SlowRetrievalError
221232

222233
raise exceptions.DownloadLengthMismatchError(
@@ -228,7 +239,7 @@ def _check_downloaded_length(
228239
# download the Timestamp or Root metadata, for which we have no
229240
# signed metadata; so, we must guess a reasonable required_length
230241
# for it.
231-
if average_download_speed < tuf.settings.MIN_AVERAGE_DOWNLOAD_SPEED:
242+
if average_download_speed < settings.MIN_AVERAGE_DOWNLOAD_SPEED:
232243
raise exceptions.SlowRetrievalError
233244

234245
logger.debug(

tuf/ngclient/_internal/requests_fetcher.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import logging
99
import time
10-
from typing import Optional
10+
from typing import Dict, Iterator, Optional
1111
from urllib import parse
1212

1313
# Imports
@@ -31,7 +31,7 @@ class RequestsFetcher(FetcherInterface):
3131
session per scheme+hostname combination.
3232
"""
3333

34-
def __init__(self):
34+
def __init__(self) -> None:
3535
# http://docs.python-requests.org/en/master/user/advanced/#session-objects:
3636
#
3737
# "The Session object allows you to persist certain parameters across
@@ -46,14 +46,14 @@ def __init__(self):
4646
# improve efficiency, but avoiding sharing state between different
4747
# hosts-scheme combinations to minimize subtle security issues.
4848
# Some cookies may not be HTTP-safe.
49-
self._sessions = {}
49+
self._sessions: Dict[str, requests.Session] = {}
5050

5151
# Default settings
5252
self.socket_timeout: int = 4 # seconds
5353
self.chunk_size: int = 400000 # bytes
5454
self.sleep_before_round: Optional[int] = None
5555

56-
def fetch(self, url, required_length):
56+
def fetch(self, url: str, required_length: int) -> Iterator[bytes]:
5757
"""Fetches the contents of HTTP/HTTPS url from a remote server.
5858
5959
Ensures the length of the downloaded data is up to 'required_length'.
@@ -93,7 +93,7 @@ def fetch(self, url, required_length):
9393
# Define a generator function to be returned by fetch. This way the
9494
# caller of fetch can differentiate between connection and actual data
9595
# download and measure download times accordingly.
96-
def chunks():
96+
def chunks() -> Iterator[bytes]:
9797
try:
9898
bytes_received = 0
9999
while True:
@@ -143,7 +143,7 @@ def chunks():
143143

144144
return chunks()
145145

146-
def _get_session(self, url):
146+
def _get_session(self, url: str) -> requests.Session:
147147
"""Returns a different customized requests.Session per schema+hostname
148148
combination.
149149
"""

tuf/ngclient/fetcher.py

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

77
# Imports
88
import abc
9+
from typing import Iterator
910

1011

1112
# Classes
@@ -20,7 +21,7 @@ class FetcherInterface:
2021
__metaclass__ = abc.ABCMeta
2122

2223
@abc.abstractmethod
23-
def fetch(self, url, required_length):
24+
def fetch(self, url: str, required_length: int) -> Iterator[bytes]:
2425
"""Fetches the contents of HTTP/HTTPS url from a remote server.
2526
2627
Ensures the length of the downloaded data is up to 'required_length'.

0 commit comments

Comments
 (0)