Skip to content

Commit a82d9b8

Browse files
committed
Feat: Add "auto" checksum option and make default
1 parent ffe8135 commit a82d9b8

File tree

9 files changed

+115
-106
lines changed

9 files changed

+115
-106
lines changed

‎google/cloud/storage/_helpers.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,6 @@ def _api_core_retry_to_resumable_media_retry(retry):
651651
def _get_invocation_id():
652652
return "gccl-invocation-id/" + str(uuid4())
653653

654-
def _detect_supported_checksums():
655-
"""Returns 'crc32c' if a fast crc32c impl. is available, else 'md5'"""
656-
657654

658655
def _get_default_headers(
659656
user_agent,

‎google/cloud/storage/_media/_download.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,22 @@ class Download(DownloadBase):
136136
"""
137137

138138
def __init__(
139-
self, media_url, stream=None, start=None, end=None, headers=None, checksum="auto"
139+
self,
140+
media_url,
141+
stream=None,
142+
start=None,
143+
end=None,
144+
headers=None,
145+
checksum="auto",
140146
):
141147
super(Download, self).__init__(
142148
media_url, stream=stream, start=start, end=end, headers=headers
143149
)
144150
self.checksum = checksum
145151
if self.checksum == "auto":
146-
self.checksum = "crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
152+
self.checksum = (
153+
"crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
154+
)
147155
self._bytes_downloaded = 0
148156
self._expected_checksum = None
149157
self._checksum_object = None

‎google/cloud/storage/_media/_helpers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import hashlib
2121
import logging
2222
import random
23-
import warnings
2423

2524
from urllib.parse import parse_qs
2625
from urllib.parse import urlencode
@@ -294,6 +293,7 @@ def _get_checksum_object(checksum_type):
294293
# In order to support platforms that don't have google_crc32c
295294
# support, only perform the import on demand.
296295
import google_crc32c
296+
297297
return google_crc32c.Checksum()
298298
elif checksum_type is None:
299299
return None
@@ -304,6 +304,7 @@ def _get_checksum_object(checksum_type):
304304
def _is_crc32c_available_and_fast():
305305
try:
306306
import google_crc32c
307+
307308
if google_crc32c.implementation == "c":
308309
return True
309310
except Exception:

‎google/cloud/storage/_media/_upload.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ def __init__(self, upload_url, headers=None, checksum="auto"):
265265
super(MultipartUpload, self).__init__(upload_url, headers=headers)
266266
self._checksum_type = checksum
267267
if self._checksum_type == "auto":
268-
self.checksum_type = "crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
268+
self._checksum_type = (
269+
"crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
270+
)
269271

270272
def _prepare_request(self, data, metadata, content_type):
271273
"""Prepare the contents of an HTTP request.
@@ -390,7 +392,9 @@ def __init__(self, upload_url, chunk_size, checksum="auto", headers=None):
390392
self._bytes_checksummed = 0
391393
self._checksum_type = checksum
392394
if self._checksum_type == "auto":
393-
self.checksum_type = "crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
395+
self._checksum_type = (
396+
"crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
397+
)
394398
self._checksum_object = None
395399
self._total_bytes = None
396400
self._resumable_url = None
@@ -1228,7 +1232,9 @@ def __init__(
12281232
self._etag = None
12291233
self._checksum_type = checksum
12301234
if self._checksum_type == "auto":
1231-
self.checksum_type = "crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
1235+
self._checksum_type = (
1236+
"crc32c" if _helpers._is_crc32c_available_and_fast() else "md5"
1237+
)
12321238
self._checksum_object = None
12331239

12341240
@property

‎tests/resumable_media/unit/requests/test_upload.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,9 @@ def test_mpu_container_cancel():
389389

390390

391391
def test_mpu_part(filename):
392-
part = upload_mod.XMLMPUPart(EXAMPLE_XML_UPLOAD_URL, UPLOAD_ID, filename, 0, 128, 1)
392+
part = upload_mod.XMLMPUPart(
393+
EXAMPLE_XML_UPLOAD_URL, UPLOAD_ID, filename, 0, 128, 1, checksum=None
394+
)
393395

394396
transport = mock.Mock(spec=["request"])
395397
transport.request.return_value = _make_response(headers={"etag": PARTS[1]})

‎tests/resumable_media/unit/test__helpers.py

Lines changed: 15 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -209,80 +209,26 @@ def test__get_checksum_object_invalid():
209209
_helpers._get_checksum_object("invalid")
210210

211211

212-
# @mock.patch("builtins.__import__")
213-
# def test__get_crc32_object_wo_google_crc32c_wo_crcmod(mock_import):
214-
# mock_import.side_effect = ImportError("testing")
212+
def test__is_crc32c_available_and_fast():
213+
import sys
215214

216-
# with pytest.raises(ImportError):
217-
# _helpers._get_crc32c_object()
215+
import google_crc32c
218216

219-
# expected_calls = [
220-
# mock.call("google_crc32c", mock.ANY, None, None, 0),
221-
# mock.call("crcmod", mock.ANY, None, None, 0),
222-
# ]
223-
# mock_import.assert_has_calls(expected_calls)
217+
assert google_crc32c.implementation == "c"
218+
assert _helpers._is_crc32c_available_and_fast() is True
224219

220+
del sys.modules["google_crc32c"]
221+
with mock.patch("builtins.__import__", side_effect=ImportError):
222+
assert _helpers._is_crc32c_available_and_fast() is False
225223

226-
# @mock.patch("builtins.__import__")
227-
# def test__get_crc32_object_w_google_crc32c(mock_import):
228-
# google_crc32c = mock.Mock(spec=["Checksum"])
229-
# mock_import.return_value = google_crc32c
224+
import google_crc32c
230225

231-
# found = _helpers._get_crc32c_object()
226+
assert google_crc32c.implementation == "c"
227+
with mock.patch("google_crc32c.implementation", new="python"):
228+
assert _helpers._is_crc32c_available_and_fast() is False
232229

233-
# assert found is google_crc32c.Checksum.return_value
234-
# google_crc32c.Checksum.assert_called_once_with()
235-
236-
# mock_import.assert_called_once_with("google_crc32c", mock.ANY, None, None, 0)
237-
238-
239-
# @mock.patch("builtins.__import__")
240-
# def test__get_crc32_object_wo_google_crc32c_w_crcmod(mock_import):
241-
# crcmod = mock.Mock(spec=["predefined", "crcmod"])
242-
# crcmod.predefined = mock.Mock(spec=["Crc"])
243-
# crcmod.crcmod = mock.Mock(spec=["_usingExtension"])
244-
# mock_import.side_effect = [ImportError("testing"), crcmod, crcmod.crcmod]
245-
246-
# found = _helpers._get_crc32c_object()
247-
248-
# assert found is crcmod.predefined.Crc.return_value
249-
# crcmod.predefined.Crc.assert_called_once_with("crc-32c")
250-
251-
# expected_calls = [
252-
# mock.call("google_crc32c", mock.ANY, None, None, 0),
253-
# mock.call("crcmod", mock.ANY, None, None, 0),
254-
# mock.call("crcmod.crcmod", mock.ANY, {}, ["_usingExtension"], 0),
255-
# ]
256-
# mock_import.assert_has_calls(expected_calls)
257-
258-
259-
@pytest.mark.filterwarnings("ignore::RuntimeWarning")
260-
@mock.patch("builtins.__import__")
261-
def test__is_fast_crcmod_wo_extension_warning(mock_import):
262-
crcmod = mock.Mock(spec=["crcmod"])
263-
crcmod.crcmod = mock.Mock(spec=["_usingExtension"])
264-
crcmod.crcmod._usingExtension = False
265-
mock_import.return_value = crcmod.crcmod
266-
267-
assert not _helpers._is_fast_crcmod()
268-
269-
mock_import.assert_called_once_with(
270-
"crcmod.crcmod",
271-
mock.ANY,
272-
{},
273-
["_usingExtension"],
274-
0,
275-
)
276-
277-
278-
@mock.patch("builtins.__import__")
279-
def test__is_fast_crcmod_w_extension(mock_import):
280-
crcmod = mock.Mock(spec=["crcmod"])
281-
crcmod.crcmod = mock.Mock(spec=["_usingExtension"])
282-
crcmod.crcmod._usingExtension = True
283-
mock_import.return_value = crcmod.crcmod
284-
285-
assert _helpers._is_fast_crcmod()
230+
# Run this again to confirm we're back to the initial state.
231+
assert _helpers._is_crc32c_available_and_fast() is True
286232

287233

288234
def test__DoNothingHash():
@@ -312,7 +258,7 @@ def _get_headers(response):
312258

313259
checksum_types = {
314260
"md5": type(hashlib.md5()),
315-
"crc32c": type(_helpers._get_crc32c_object()),
261+
"crc32c": type(google_crc32c.Checksum()),
316262
}
317263
assert isinstance(checksum_obj, checksum_types[checksum])
318264

‎tests/resumable_media/unit/test__upload.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def test_constructor_defaults(self):
191191
upload = _upload.MultipartUpload(MULTIPART_URL)
192192
assert upload.upload_url == MULTIPART_URL
193193
assert upload._headers == {}
194-
assert upload._checksum_type is None
194+
assert upload._checksum_type == "crc32c" # converted from "auto"
195195
assert not upload._finished
196196
_check_retry_strategy(upload)
197197

@@ -204,6 +204,17 @@ def test_constructor_explicit(self):
204204
assert not upload._finished
205205
_check_retry_strategy(upload)
206206

207+
def test_constructor_explicit_auto(self):
208+
headers = {"spin": "doctors"}
209+
upload = _upload.MultipartUpload(
210+
MULTIPART_URL, headers=headers, checksum="auto"
211+
)
212+
assert upload.upload_url == MULTIPART_URL
213+
assert upload._headers is headers
214+
assert upload._checksum_type == "crc32c"
215+
assert not upload._finished
216+
_check_retry_strategy(upload)
217+
207218
def test__prepare_request_already_finished(self):
208219
upload = _upload.MultipartUpload(MULTIPART_URL)
209220
upload._finished = True
@@ -345,7 +356,7 @@ def test_constructor(self):
345356
assert upload._checksum_object is None
346357
assert upload._total_bytes is None
347358
assert upload._resumable_url is None
348-
assert upload._checksum_type is None
359+
assert upload._checksum_type == "crc32c" # converted from "auto"
349360

350361
def test_constructor_bad_chunk_size(self):
351362
with pytest.raises(ValueError):
@@ -771,7 +782,7 @@ def test__process_resumable_response_bad_status(self):
771782
assert upload.invalid
772783

773784
def test__process_resumable_response_success(self):
774-
upload = _upload.ResumableUpload(RESUMABLE_URL, ONE_MB)
785+
upload = _upload.ResumableUpload(RESUMABLE_URL, ONE_MB, checksum=None)
775786
_fix_up_virtual(upload)
776787

777788
# Check / set status before.
@@ -1401,6 +1412,28 @@ def test_xml_mpu_part(filename):
14011412
END,
14021413
PART_NUMBER,
14031414
headers=EXAMPLE_HEADERS,
1415+
checksum="auto",
1416+
)
1417+
assert part.upload_url == EXAMPLE_XML_UPLOAD_URL
1418+
assert part.upload_id == UPLOAD_ID
1419+
assert part.filename == filename
1420+
assert part.etag is None
1421+
assert part.start == START
1422+
assert part.end == END
1423+
assert part.part_number == PART_NUMBER
1424+
assert part._headers == EXAMPLE_HEADERS
1425+
assert part._checksum_type == "crc32c" # transformed from "auto"
1426+
assert part._checksum_object is None
1427+
1428+
part = _upload.XMLMPUPart(
1429+
EXAMPLE_XML_UPLOAD_URL,
1430+
UPLOAD_ID,
1431+
filename,
1432+
START,
1433+
END,
1434+
PART_NUMBER,
1435+
headers=EXAMPLE_HEADERS,
1436+
checksum=None,
14041437
)
14051438
verb, url, payload, headers = part._prepare_upload_request()
14061439
assert verb == _upload._PUT

0 commit comments

Comments
 (0)