Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swapping out google.streaming for google-resumable-media in GCS downloads. #3323

Merged
merged 3 commits into from
Apr 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 40 additions & 22 deletions storage/google/cloud/storage/blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
import six
from six.moves.urllib.parse import quote

import google.auth.transport.requests
from google import resumable_media

from google.cloud._helpers import _rfc3339_to_datetime
from google.cloud._helpers import _to_bytes
from google.cloud._helpers import _bytes_to_unicode
Expand All @@ -42,12 +45,13 @@
from google.cloud.storage.acl import ObjectACL
from google.cloud.streaming.http_wrapper import Request
from google.cloud.streaming.http_wrapper import make_api_request
from google.cloud.streaming.transfer import Download
from google.cloud.streaming.transfer import RESUMABLE_UPLOAD
from google.cloud.streaming.transfer import Upload


_API_ACCESS_ENDPOINT = 'https://storage.googleapis.com'
_DOWNLOAD_URL_TEMPLATE = (
u'https://www.googleapis.com/download/storage/v1{path}?alt=media')


class Blob(_PropertyMixin):
Expand Down Expand Up @@ -316,6 +320,24 @@ def delete(self, client=None):
"""
return self.bucket.delete_blob(self.name, client=client)

def _get_download_url(self):
"""Get the download URL for the current blob.

If the ``media_link`` has been loaded, it will be used, otherwise
the URL will be constructed from the current blob's path (and possibly
generation) to avoid a round trip.

:rtype: str
:returns: The download URL for the current blob.
"""
if self.media_link is None:
download_url = _DOWNLOAD_URL_TEMPLATE.format(path=self.path)
if self.generation is not None:
download_url += u'&generation={:d}'.format(self.generation)
return download_url
else:
return self.media_link

def download_to_file(self, file_obj, client=None):
"""Download the contents of this blob into a file-like object.

Expand Down Expand Up @@ -348,28 +370,24 @@ def download_to_file(self, file_obj, client=None):
:raises: :class:`google.cloud.exceptions.NotFound`
"""
client = self._require_client(client)
if self.media_link is None: # not yet loaded
self.reload()

download_url = self.media_link

# Use apitools 'Download' facility.
download = Download.from_stream(file_obj)

if self.chunk_size is not None:
download.chunksize = self.chunk_size

# Get the download URL.
download_url = self._get_download_url()

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

# Get any extra headers for the request.
headers = _get_encryption_headers(self._encryption_key)

request = Request(download_url, 'GET', headers)

# Use ``_base_connection`` rather ``_connection`` since the current
# connection may be a batch. A batch wraps a client's connection,
# but does not store the ``http`` object. The rest (API_BASE_URL and
# build_api_url) are also defined on the Batch class, but we just
# use the wrapped connection since it has all three (http,
# API_BASE_URL and build_api_url).
download.initialize_download(request, client._base_connection.http)
# Create a ``requests`` transport with the client's credentials.
transport = google.auth.transport.requests.AuthorizedSession(
client._credentials)

# Download the content.
if self.chunk_size is None:
download = resumable_media.Download(download_url, headers=headers)
response = download.consume(transport)
file_obj.write(response.content)

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

else:
download = resumable_media.ChunkedDownload(
download_url, self.chunk_size, file_obj, headers=headers)
while not download.finished:
download.consume_next_chunk(transport)

def download_to_filename(self, filename, client=None):
"""Download the contents of this blob into a named file.
Expand Down
2 changes: 1 addition & 1 deletion storage/google/cloud/storage/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def __init__(self, client, name=None):
self._default_object_acl = DefaultObjectACL(self)

def __repr__(self):
return '<Bucket: %s>' % self.name
return '<Bucket: %s>' % (self.name,)

@property
def client(self):
Expand Down
3 changes: 2 additions & 1 deletion storage/nox.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def unit_tests(session, python_version):
session.install('-e', '.')

# Run py.test against the unit tests.
session.run('py.test', '--quiet',
session.run(
'py.test', '--quiet',
'--cov=google.cloud.storage', '--cov=tests.unit', '--cov-append',
'--cov-config=.coveragerc', '--cov-report=', '--cov-fail-under=97',
'tests/unit',
Expand Down
3 changes: 3 additions & 0 deletions storage/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@

REQUIREMENTS = [
'google-cloud-core >= 0.24.0, < 0.25dev',
'google-auth >= 1.0.0',
'google-resumable-media == 0.0.2',
'requests >= 2.0.0',
]

setup(
Expand Down
Loading