diff --git a/google/resumable_media/_download.py b/google/resumable_media/_download.py index ba31cf4a..7f489d2e 100644 --- a/google/resumable_media/_download.py +++ b/google/resumable_media/_download.py @@ -37,6 +37,8 @@ class DownloadBase(object): Args: media_url (str): The URL containing the media to be downloaded. + stream (IO[bytes]): A write-able stream (i.e. file-like object) that + the downloaded resource can be written to. start (int): The first byte in a range to be downloaded. end (int): The last byte in a range to be downloaded. headers (Optional[Mapping[str, str]]): Extra headers that should @@ -48,8 +50,10 @@ class DownloadBase(object): end (Optional[int]): The last byte in a range to be downloaded. """ - def __init__(self, media_url, start=None, end=None, headers=None): + def __init__(self, media_url, stream=None, + start=None, end=None, headers=None): self.media_url = media_url + self._stream = stream self.start = start self.end = end if headers is None: @@ -109,6 +113,8 @@ class Download(DownloadBase): Args: media_url (str): The URL containing the media to be downloaded. + stream (IO[bytes]): A write-able stream (i.e. file-like object) that + the downloaded resource can be written to. start (int): The first byte in a range to be downloaded. If not provided, but ``end`` is provided, will download from the beginning to ``end`` of the media. @@ -166,6 +172,9 @@ def _process_response(self, response): def consume(self, transport): """Consume the resource to be downloaded. + If a ``stream`` is attached to this download, then the downloaded + resource will be written to the stream. + Args: transport (object): An object which can make authenticated requests. @@ -211,9 +220,8 @@ def __init__(self, media_url, chunk_size, stream, u'On a chunked download the starting ' u'value cannot be negative.') super(ChunkedDownload, self).__init__( - media_url, start=start, end=end, headers=headers) + media_url, stream=stream, start=start, end=end, headers=headers) self.chunk_size = chunk_size - self._stream = stream self._bytes_downloaded = 0 self._total_bytes = None self._invalid = False diff --git a/google/resumable_media/requests/download.py b/google/resumable_media/requests/download.py index aaf3676a..26935cfc 100644 --- a/google/resumable_media/requests/download.py +++ b/google/resumable_media/requests/download.py @@ -28,6 +28,8 @@ class Download(_helpers.RequestsMixin, _download.Download): Args: media_url (str): The URL containing the media to be downloaded. + stream (IO[bytes]): A write-able stream (i.e. file-like object) that + the downloaded resource can be written to. start (int): The first byte in a range to be downloaded. If not provided, but ``end`` is provided, will download from the beginning to ``end`` of the media. diff --git a/tests/unit/test__download.py b/tests/unit/test__download.py index 8c4a32e1..ee4e9fc7 100644 --- a/tests/unit/test__download.py +++ b/tests/unit/test__download.py @@ -32,6 +32,7 @@ class TestDownloadBase(object): def test_constructor_defaults(self): download = _download.DownloadBase(EXAMPLE_URL) assert download.media_url == EXAMPLE_URL + assert download._stream is None assert download.start is None assert download.end is None assert download._headers == {} @@ -43,8 +44,10 @@ def test_constructor_explicit(self): end = 10001 headers = {u'foof': u'barf'} download = _download.DownloadBase( - EXAMPLE_URL, start=start, end=end, headers=headers) + EXAMPLE_URL, stream=mock.sentinel.stream, + start=start, end=end, headers=headers) assert download.media_url == EXAMPLE_URL + assert download._stream is mock.sentinel.stream assert download.start == start assert download.end == end assert download._headers is headers