Skip to content

Commit 687506f

Browse files
authored
Merge pull request #1147 from jku/close-all-the-things
Close file objects, requests.Responses
2 parents 87e92d5 + fb9d8e7 commit 687506f

File tree

4 files changed

+55
-72
lines changed

4 files changed

+55
-72
lines changed

tests/test_download.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,11 @@ def tearDown(self):
105105
def test_download_url_to_tempfileobj(self):
106106

107107
download_file = download.safe_download
108-
109-
temp_fileobj = download_file(self.url, self.target_data_length)
110-
temp_fileobj.seek(0)
111-
temp_file_data = temp_fileobj.read().decode('utf-8')
112-
self.assertEqual(self.target_data, temp_file_data)
113-
self.assertEqual(self.target_data_length, len(temp_file_data))
114-
temp_fileobj.close()
108+
with download_file(self.url, self.target_data_length) as temp_fileobj:
109+
temp_fileobj.seek(0)
110+
temp_file_data = temp_fileobj.read().decode('utf-8')
111+
self.assertEqual(self.target_data, temp_file_data)
112+
self.assertEqual(self.target_data_length, len(temp_file_data))
115113

116114

117115

@@ -344,16 +342,16 @@ def test_https_connection(self):
344342
# TODO: Confirm necessity of this session clearing and lay out mechanics.
345343
tuf.download._sessions = {}
346344
logger.info('Trying HTTPS download of target file: ' + good_https_url)
347-
download.safe_download(good_https_url, target_data_length)
348-
download.unsafe_download(good_https_url, target_data_length)
345+
download.safe_download(good_https_url, target_data_length).close()
346+
download.unsafe_download(good_https_url, target_data_length).close()
349347

350348
os.environ['REQUESTS_CA_BUNDLE'] = good2_cert_fname
351349
# Clear sessions to ensure that the certificate we just specified is used.
352350
# TODO: Confirm necessity of this session clearing and lay out mechanics.
353351
tuf.download._sessions = {}
354352
logger.info('Trying HTTPS download of target file: ' + good2_https_url)
355-
download.safe_download(good2_https_url, target_data_length)
356-
download.unsafe_download(good2_https_url, target_data_length)
353+
download.safe_download(good2_https_url, target_data_length).close()
354+
download.unsafe_download(good2_https_url, target_data_length).close()
357355

358356
finally:
359357
for proc in [

tests/test_updater.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1627,32 +1627,32 @@ def test_9__get_target_hash(self):
16271627

16281628
def test_10__hard_check_file_length(self):
16291629
# Test for exception if file object is not equal to trusted file length.
1630-
temp_file_object = tempfile.TemporaryFile()
1631-
temp_file_object.write(b'X')
1632-
temp_file_object.seek(0)
1633-
self.assertRaises(tuf.exceptions.DownloadLengthMismatchError,
1634-
self.repository_updater._hard_check_file_length,
1635-
temp_file_object, 10)
1630+
with tempfile.TemporaryFile() as temp_file_object:
1631+
temp_file_object.write(b'X')
1632+
temp_file_object.seek(0)
1633+
self.assertRaises(tuf.exceptions.DownloadLengthMismatchError,
1634+
self.repository_updater._hard_check_file_length,
1635+
temp_file_object, 10)
16361636

16371637

16381638

16391639

16401640

16411641
def test_10__soft_check_file_length(self):
16421642
# Test for exception if file object is not equal to trusted file length.
1643-
temp_file_object = tempfile.TemporaryFile()
1644-
temp_file_object.write(b'XXX')
1645-
temp_file_object.seek(0)
1646-
self.assertRaises(tuf.exceptions.DownloadLengthMismatchError,
1647-
self.repository_updater._soft_check_file_length,
1648-
temp_file_object, 1)
1643+
with tempfile.TemporaryFile() as temp_file_object:
1644+
temp_file_object.write(b'XXX')
1645+
temp_file_object.seek(0)
1646+
self.assertRaises(tuf.exceptions.DownloadLengthMismatchError,
1647+
self.repository_updater._soft_check_file_length,
1648+
temp_file_object, 1)
16491649

1650-
# Verify that an exception is not raised if the file length <= the observed
1651-
# file length.
1652-
temp_file_object.seek(0)
1653-
self.repository_updater._soft_check_file_length(temp_file_object, 3)
1654-
temp_file_object.seek(0)
1655-
self.repository_updater._soft_check_file_length(temp_file_object, 4)
1650+
# Verify that an exception is not raised if the file length <= the observed
1651+
# file length.
1652+
temp_file_object.seek(0)
1653+
self.repository_updater._soft_check_file_length(temp_file_object, 3)
1654+
temp_file_object.seek(0)
1655+
self.repository_updater._soft_check_file_length(temp_file_object, 4)
16561656

16571657

16581658

@@ -1763,14 +1763,13 @@ def test_10__visit_child_role(self):
17631763

17641764
def test_11__verify_metadata_file(self):
17651765
# Test for invalid metadata content.
1766-
metadata_file_object = tempfile.TemporaryFile()
1767-
metadata_file_object.write(b'X')
1768-
metadata_file_object.seek(0)
1769-
1770-
self.assertRaises(tuf.exceptions.InvalidMetadataJSONError,
1771-
self.repository_updater._verify_metadata_file,
1772-
metadata_file_object, 'root')
1766+
with tempfile.TemporaryFile() as metadata_file_object:
1767+
metadata_file_object.write(b'X')
1768+
metadata_file_object.seek(0)
17731769

1770+
self.assertRaises(tuf.exceptions.InvalidMetadataJSONError,
1771+
self.repository_updater._verify_metadata_file,
1772+
metadata_file_object, 'root')
17741773

17751774

17761775
def test_12__get_file(self):
@@ -1788,10 +1787,10 @@ def verify_target_file(targets_path):
17881787
self.repository_updater._check_hashes(targets_path, file_hashes)
17891788

17901789
self.repository_updater._get_file('targets.json', verify_target_file,
1791-
file_type, file_size, download_safely=True)
1790+
file_type, file_size, download_safely=True).close()
17921791

17931792
self.repository_updater._get_file('targets.json', verify_target_file,
1794-
file_type, file_size, download_safely=False)
1793+
file_type, file_size, download_safely=False).close()
17951794

17961795
def test_13__targets_of_role(self):
17971796
# Test case where a list of targets is given. By default, the 'targets'

tuf/client/updater.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,7 +1637,9 @@ def _get_metadata_file(self, metadata_role, remote_filename,
16371637
# Remember the error from this mirror, and "reset" the target file.
16381638
logger.exception('Update failed from ' + file_mirror + '.')
16391639
file_mirror_errors[file_mirror] = exception
1640-
file_object = None
1640+
if file_object:
1641+
file_object.close()
1642+
file_object = None
16411643

16421644
else:
16431645
break
@@ -3281,15 +3283,9 @@ def download_target(self, target, destination_directory,
32813283
trusted_length = target['fileinfo']['length']
32823284
trusted_hashes = target['fileinfo']['hashes']
32833285

3284-
# '_get_target_file()' checks every mirror and returns the first target
3285-
# that passes verification.
3286-
target_file_object = self._get_target_file(target_filepath, trusted_length,
3287-
trusted_hashes, prefix_filename_with_hash)
3288-
3289-
# We acquired a target file object from a mirror. Move the file into place
3290-
# (i.e., locally to 'destination_directory'). Note: join() discards
3291-
# 'destination_directory' if 'target_path' contains a leading path
3292-
# separator (i.e., is treated as an absolute path).
3286+
# Build absolute 'destination' file path.
3287+
# Note: join() discards 'destination_directory' if 'target_path' contains
3288+
# a leading path separator (i.e., is treated as an absolute path).
32933289
destination = os.path.join(destination_directory,
32943290
target_filepath.lstrip(os.sep))
32953291
destination = os.path.abspath(destination)
@@ -3310,4 +3306,9 @@ def download_target(self, target, destination_directory,
33103306
else:
33113307
raise
33123308

3309+
# '_get_target_file()' checks every mirror and returns the first target
3310+
# that passes verification.
3311+
target_file_object = self._get_target_file(target_filepath, trusted_length,
3312+
trusted_hashes, prefix_filename_with_hash)
3313+
33133314
securesystemslib.util.persist_temp_file(target_file_object, destination)

tuf/download.py

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -248,27 +248,20 @@ def _download_file(url, required_length, STRICT_REQUIRED_LENGTH=True):
248248

249249
# Get the requests.Response object for this URL.
250250
#
251-
# Always stream to control how requests are downloaded:
252-
# http://docs.python-requests.org/en/master/user/advanced/#body-content-workflow
253-
#
254-
# We will always manually close Responses, so no need for a context
255-
# manager.
256-
#
251+
# Defer downloading the response body with stream=True.
257252
# Always set the timeout. This timeout value is interpreted by requests as:
258253
# - connect timeout (max delay before first byte is received)
259254
# - read (gap) timeout (max delay between bytes received)
260-
# These are NOT overall/total, wall-clock timeouts for any single read.
261-
# http://docs.python-requests.org/en/master/user/advanced/#timeouts
262-
response = session.get(
263-
url, stream=True, timeout=tuf.settings.SOCKET_TIMEOUT)
255+
with session.get(url, stream=True,
256+
timeout=tuf.settings.SOCKET_TIMEOUT) as response:
264257

265-
# Check response status.
266-
response.raise_for_status()
258+
# Check response status.
259+
response.raise_for_status()
267260

268-
# Download the contents of the URL, up to the required length, to a
269-
# temporary file, and get the total number of downloaded bytes.
270-
total_downloaded, average_download_speed = \
271-
_download_fixed_amount_of_data(response, temp_file, required_length)
261+
# Download the contents of the URL, up to the required length, to a
262+
# temporary file, and get the total number of downloaded bytes.
263+
total_downloaded, average_download_speed = \
264+
_download_fixed_amount_of_data(response, temp_file, required_length)
272265

273266
# Does the total number of downloaded bytes match the required length?
274267
_check_downloaded_length(total_downloaded, required_length,
@@ -382,16 +375,8 @@ def _download_fixed_amount_of_data(response, temp_file, required_length):
382375
break
383376

384377
except urllib3.exceptions.ReadTimeoutError as e:
385-
# Whatever happens, make sure that we always close the connection.
386-
response.close()
387378
raise tuf.exceptions.SlowRetrievalError(str(e))
388379

389-
except:
390-
# Whatever happens, make sure that we always close the connection.
391-
response.close()
392-
raise
393-
394-
response.close()
395380
return number_of_bytes_received, average_download_speed
396381

397382

0 commit comments

Comments
 (0)