Skip to content

Commit

Permalink
Updating Blob.download* methods to accept a client.
Browse files Browse the repository at this point in the history
Towards googleapis#952, removing connection from methods / constructors.

Also adding a temporary Blob._client_or_connection method
to allow switching from an explicit client to an implicit
connection.
  • Loading branch information
dhermes committed Jul 13, 2015
1 parent 3905a5b commit 301bc1c
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 25 deletions.
56 changes: 34 additions & 22 deletions gcloud/storage/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,24 @@ def generate_signed_url(self, expiration, method='GET',
api_access_endpoint=_API_ACCESS_ENDPOINT,
expiration=expiration, method=method)

@staticmethod
def _client_or_connection(client):
"""Temporary method to get a connection from a client.
If the client is null, gets the connection from the environment.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:rtype: :class:`gcloud.storage.connection.Connection`
:returns: The connection determined from the ``client`` or environment.
"""
if client is None:
return _require_connection()
else:
return client.connection

def exists(self, client=None):
"""Determines whether or not this blob exists.
Expand All @@ -210,10 +228,7 @@ def exists(self, client=None):
:rtype: boolean
:returns: True if the blob exists in Cloud Storage.
"""
if client is None:
connection = _require_connection()
else:
connection = client.connection
connection = self._client_or_connection(client)
try:
# We only need the status code (200 or not) so we seek to
# minimize the returned payload.
Expand Down Expand Up @@ -275,20 +290,19 @@ def delete(self, connection=None):
connection = _require_connection(connection)
return self.bucket.delete_blob(self.name, connection=connection)

def download_to_file(self, file_obj, connection=None):
def download_to_file(self, file_obj, client=None):
"""Download the contents of this blob into a file-like object.
:type file_obj: file
:param file_obj: A file handle to which to write the blob's data.
:type connection: :class:`gcloud.storage.connection.Connection` or
``NoneType``
:param connection: Optional. The connection to use when sending
requests. If not provided, falls back to default.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:raises: :class:`gcloud.exceptions.NotFound`
"""
connection = _require_connection(connection)
connection = self._client_or_connection(client)
download_url = self.media_link

# Use apitools 'Download' facility.
Expand All @@ -307,39 +321,37 @@ def download_to_file(self, file_obj, connection=None):
download.StreamInChunks(callback=lambda *args: None,
finish_callback=lambda *args: None)

def download_to_filename(self, filename, connection=None):
def download_to_filename(self, filename, client=None):
"""Download the contents of this blob into a named file.
:type filename: string
:param filename: A filename to be passed to ``open``.
:type connection: :class:`gcloud.storage.connection.Connection` or
``NoneType``
:param connection: Optional. The connection to use when sending
requests. If not provided, falls back to default.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:raises: :class:`gcloud.exceptions.NotFound`
"""
with open(filename, 'wb') as file_obj:
self.download_to_file(file_obj, connection=connection)
self.download_to_file(file_obj, client=client)

mtime = time.mktime(self.updated.timetuple())
os.utime(file_obj.name, (mtime, mtime))

def download_as_string(self, connection=None):
def download_as_string(self, client=None):
"""Download the contents of this blob as a string.
:type connection: :class:`gcloud.storage.connection.Connection` or
``NoneType``
:param connection: Optional. The connection to use when sending
requests. If not provided, falls back to default.
:type client: :class:`gcloud.storage.client.Client` or ``NoneType``
:param client: Optional. The client to use. If not passed, falls back
to default connection.
:rtype: bytes
:returns: The data stored in this blob.
:raises: :class:`gcloud.exceptions.NotFound`
"""
string_buffer = BytesIO()
self.download_to_file(string_buffer, connection=connection)
self.download_to_file(string_buffer, client=client)
return string_buffer.getvalue()

def upload_from_file(self, file_obj, rewind=False, size=None,
Expand Down
9 changes: 6 additions & 3 deletions gcloud/storage/test_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ def _download_to_file_helper(self, chunk_size=None):
(chunk1_response, b'abc'),
(chunk2_response, b'def'),
)
client = _Client(connection)
bucket = _Bucket()
MEDIA_LINK = 'http://example.com/media/'
properties = {'mediaLink': MEDIA_LINK}
Expand All @@ -363,7 +364,7 @@ def _download_to_file_helper(self, chunk_size=None):
blob._CHUNK_SIZE_MULTIPLE = 1
blob.chunk_size = chunk_size
fh = BytesIO()
blob.download_to_file(fh, connection=connection)
blob.download_to_file(fh, client=client)
self.assertEqual(fh.getvalue(), b'abcdef')

def test_download_to_file_default(self):
Expand All @@ -387,6 +388,7 @@ def test_download_to_filename(self):
(chunk1_response, b'abc'),
(chunk2_response, b'def'),
)
client = _Client(connection)
bucket = _Bucket()
MEDIA_LINK = 'http://example.com/media/'
properties = {'mediaLink': MEDIA_LINK,
Expand All @@ -395,7 +397,7 @@ def test_download_to_filename(self):
blob._CHUNK_SIZE_MULTIPLE = 1
blob.chunk_size = 3
with NamedTemporaryFile() as f:
blob.download_to_filename(f.name, connection=connection)
blob.download_to_filename(f.name, client=client)
f.flush()
with open(f.name, 'rb') as g:
wrote = g.read()
Expand All @@ -416,13 +418,14 @@ def test_download_as_string(self):
(chunk1_response, b'abc'),
(chunk2_response, b'def'),
)
client = _Client(connection)
bucket = _Bucket()
MEDIA_LINK = 'http://example.com/media/'
properties = {'mediaLink': MEDIA_LINK}
blob = self._makeOne(BLOB_NAME, bucket=bucket, properties=properties)
blob._CHUNK_SIZE_MULTIPLE = 1
blob.chunk_size = 3
fetched = blob.download_as_string(connection=connection)
fetched = blob.download_as_string(client=client)
self.assertEqual(fetched, b'abcdef')

def test_upload_from_file_size_failure(self):
Expand Down

0 comments on commit 301bc1c

Please sign in to comment.