Skip to content

Commit cba090e

Browse files
committed
Merge pull request #1559 from tseaver/1555-reload_on_missing_media_link
#1555: reload missing properties before blob download
2 parents 63aa65c + 4f16cf5 commit cba090e

File tree

2 files changed

+43
-8
lines changed

2 files changed

+43
-8
lines changed

gcloud/storage/blob.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ def delete(self, client=None):
277277
def download_to_file(self, file_obj, client=None):
278278
"""Download the contents of this blob into a file-like object.
279279
280+
.. note::
281+
282+
If the server-set property, :attr:`media_link`, is not yet
283+
initialized, makes an additional API request to load it.
284+
280285
:type file_obj: file
281286
:param file_obj: A file handle to which to write the blob's data.
282287
@@ -287,6 +292,9 @@ def download_to_file(self, file_obj, client=None):
287292
:raises: :class:`gcloud.exceptions.NotFound`
288293
"""
289294
client = self._require_client(client)
295+
if self.media_link is None: # not yet loaded
296+
self.reload()
297+
290298
download_url = self.media_link
291299

292300
# Use apitools 'Download' facility.

gcloud/storage/test_blob.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def test_generate_signed_url_w_method_arg(self):
260260
def test_exists_miss(self):
261261
from six.moves.http_client import NOT_FOUND
262262
NONESUCH = 'nonesuch'
263-
not_found_response = {'status': NOT_FOUND}
263+
not_found_response = ({'status': NOT_FOUND}, b'')
264264
connection = _Connection(not_found_response)
265265
client = _Client(connection)
266266
bucket = _Bucket(client)
@@ -270,7 +270,7 @@ def test_exists_miss(self):
270270
def test_exists_hit(self):
271271
from six.moves.http_client import OK
272272
BLOB_NAME = 'blob-name'
273-
found_response = {'status': OK}
273+
found_response = ({'status': OK}, b'')
274274
connection = _Connection(found_response)
275275
client = _Client(connection)
276276
bucket = _Bucket(client)
@@ -281,7 +281,7 @@ def test_exists_hit(self):
281281
def test_delete(self):
282282
from six.moves.http_client import NOT_FOUND
283283
BLOB_NAME = 'blob-name'
284-
not_found_response = {'status': NOT_FOUND}
284+
not_found_response = ({'status': NOT_FOUND}, b'')
285285
connection = _Connection(not_found_response)
286286
client = _Client(connection)
287287
bucket = _Bucket(client)
@@ -291,6 +291,32 @@ def test_delete(self):
291291
self.assertFalse(blob.exists())
292292
self.assertEqual(bucket._deleted, [(BLOB_NAME, None)])
293293

294+
def test_download_to_file_wo_media_link(self):
295+
from six.moves.http_client import OK
296+
from six.moves.http_client import PARTIAL_CONTENT
297+
from io import BytesIO
298+
BLOB_NAME = 'blob-name'
299+
MEDIA_LINK = 'http://example.com/media/'
300+
chunk1_response = {'status': PARTIAL_CONTENT,
301+
'content-range': 'bytes 0-2/6'}
302+
chunk2_response = {'status': OK,
303+
'content-range': 'bytes 3-5/6'}
304+
connection = _Connection(
305+
(chunk1_response, b'abc'),
306+
(chunk2_response, b'def'),
307+
)
308+
# Only the 'reload' request hits on this side: the others are done
309+
# through the 'http' object.
310+
reload_response = {'status': OK, 'content-type': 'application/json'}
311+
connection._responses = [(reload_response, {"mediaLink": MEDIA_LINK})]
312+
client = _Client(connection)
313+
bucket = _Bucket(client)
314+
blob = self._makeOne(BLOB_NAME, bucket=bucket)
315+
fh = BytesIO()
316+
blob.download_to_file(fh)
317+
self.assertEqual(fh.getvalue(), b'abcdef')
318+
self.assertEqual(blob.media_link, MEDIA_LINK)
319+
294320
def _download_to_file_helper(self, chunk_size=None):
295321
from six.moves.http_client import OK
296322
from six.moves.http_client import PARTIAL_CONTENT
@@ -749,10 +775,11 @@ def test_upload_from_string_w_text(self):
749775
self.assertEqual(rq[0]['body'], ENCODED)
750776

751777
def test_make_public(self):
778+
from six.moves.http_client import OK
752779
from gcloud.storage.acl import _ACLEntity
753780
BLOB_NAME = 'blob-name'
754781
permissive = [{'entity': 'allUsers', 'role': _ACLEntity.READER_ROLE}]
755-
after = {'acl': permissive}
782+
after = ({'status': OK}, {'acl': permissive})
756783
connection = _Connection(after)
757784
client = _Client(connection)
758785
bucket = _Bucket(client=client)
@@ -1092,10 +1119,10 @@ def __init__(self, *responses):
10921119
def api_request(self, **kw):
10931120
from six.moves.http_client import NOT_FOUND
10941121
from gcloud.exceptions import NotFound
1095-
result = self._respond(**kw)
1096-
if result.get('status') == NOT_FOUND:
1097-
raise NotFound(result)
1098-
return result
1122+
info, content = self._respond(**kw)
1123+
if info.get('status') == NOT_FOUND:
1124+
raise NotFound(info)
1125+
return content
10991126

11001127
def build_api_url(self, path, query_params=None,
11011128
api_base_url=API_BASE_URL):

0 commit comments

Comments
 (0)