Hello community,
here is the log from the commit of package python-google-resumable-media for
openSUSE:Factory checked in at 2019-09-18 13:09:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-google-resumable-media (Old)
and /work/SRC/openSUSE:Factory/.python-google-resumable-media.new.7948
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-resumable-media"
Wed Sep 18 13:09:18 2019 rev:4 rq:730356 version:0.4.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-google-resumable-media/python-google-resumable-media.changes
2019-03-19 09:58:33.376099014 +0100
+++
/work/SRC/openSUSE:Factory/.python-google-resumable-media.new.7948/python-google-resumable-media.changes
2019-09-18 13:09:23.628715723 +0200
@@ -1,0 +2,10 @@
+Thu Sep 12 10:14:02 UTC 2019 - Tomáš Chvátal <[email protected]>
+
+- Update to 0.4.0:
+ * Require 200 response for initial resumable upload request. (#95)
+ * Use response as variable for object returned from http_request. (#98)
+ * Further DRY request dependency pins. (#96)
+ * Finish download on seeing 416 response with zero byte range. (#86)
+ * Always use raw response data. (#87)
+
+-------------------------------------------------------------------
Old:
----
google-resumable-media-0.3.2.tar.gz
New:
----
google-resumable-media-0.4.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-google-resumable-media.spec ++++++
--- /var/tmp/diff_new_pack.SE7Ngz/_old 2019-09-18 13:09:24.128715618 +0200
+++ /var/tmp/diff_new_pack.SE7Ngz/_new 2019-09-18 13:09:24.132715617 +0200
@@ -18,7 +18,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-google-resumable-media
-Version: 0.3.2
+Version: 0.4.0
Release: 0
Summary: Utilities for Google Media Downloads and Resumable Uploads
License: Apache-2.0
@@ -34,7 +34,7 @@
BuildRequires: fdupes
BuildRequires: python-rpm-macros
Requires: python-six
-Suggests: python-requests >= 2.18.0
+Recommends: python-requests >= 2.18.0
BuildArch: noarch
%python_subpackages
++++++ google-resumable-media-0.3.2.tar.gz ->
google-resumable-media-0.4.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-resumable-media-0.3.2/PKG-INFO
new/google-resumable-media-0.4.0/PKG-INFO
--- old/google-resumable-media-0.3.2/PKG-INFO 2018-12-20 23:28:49.000000000
+0100
+++ new/google-resumable-media-0.4.0/PKG-INFO 2019-09-06 18:49:51.000000000
+0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: google-resumable-media
-Version: 0.3.2
+Version: 0.4.0
Summary: Utilities for Google Media Downloads and Resumable Uploads
Home-page: https://github.com/GoogleCloudPlatform/google-resumable-media-python
Author: Google Cloud Platform
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google/resumable_media/_download.py
new/google-resumable-media-0.4.0/google/resumable_media/_download.py
--- old/google-resumable-media-0.3.2/google/resumable_media/_download.py
2018-12-20 20:33:22.000000000 +0100
+++ new/google-resumable-media-0.4.0/google/resumable_media/_download.py
2019-09-06 18:48:30.000000000 +0200
@@ -28,6 +28,7 @@
flags=re.IGNORECASE)
_ACCEPTABLE_STATUS_CODES = (http_client.OK, http_client.PARTIAL_CONTENT)
_GET = u'GET'
+_ZERO_CONTENT_RANGE_HEADER = u'bytes */0'
class DownloadBase(object):
@@ -340,21 +341,38 @@
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
# Verify the response before updating the current instance.
+ if _check_for_zero_content_range(response, self._get_status_code,
+ self._get_headers):
+ self._finished = True
+ return
+
_helpers.require_status_code(
response, _ACCEPTABLE_STATUS_CODES,
self._get_status_code, callback=self._make_invalid)
- content_length = _helpers.header_required(
- response, u'content-length', self._get_headers,
- callback=self._make_invalid)
- num_bytes = int(content_length)
- _, end_byte, total_bytes = get_range_info(
- response, self._get_headers, callback=self._make_invalid)
+ headers = self._get_headers(response)
response_body = self._get_body(response)
- if len(response_body) != num_bytes:
- self._make_invalid()
- raise common.InvalidResponse(
- response, u'Response is different size than content-length',
- u'Expected', num_bytes, u'Received', len(response_body))
+
+ start_byte, end_byte, total_bytes = get_range_info(
+ response, self._get_headers, callback=self._make_invalid)
+
+ transfer_encoding = headers.get(u'transfer-encoding')
+
+ if transfer_encoding is None:
+ content_length = _helpers.header_required(
+ response, u'content-length', self._get_headers,
+ callback=self._make_invalid)
+ num_bytes = int(content_length)
+ if len(response_body) != num_bytes:
+ self._make_invalid()
+ raise common.InvalidResponse(
+ response,
+ u'Response is different size than content-length',
+ u'Expected', num_bytes,
+ u'Received', len(response_body),
+ )
+ else:
+ # 'content-length' header not allowed with chunked encoding.
+ num_bytes = end_byte - start_byte + 1
# First update ``bytes_downloaded``.
self._bytes_downloaded += num_bytes
@@ -466,3 +484,28 @@
int(match.group(u'end_byte')),
int(match.group(u'total_bytes'))
)
+
+
+def _check_for_zero_content_range(response, get_status_code, get_headers):
+ """ Validate if response status code is 416 and content range is zero.
+
+ This is the special case for handling zero bytes files.
+
+ Args:
+ response (object): An HTTP response object.
+ get_status_code (Callable[Any, int]): Helper to get a status code
+ from a response.
+ get_headers (Callable[Any, Mapping[str, str]]): Helper to get headers
+ from an HTTP response.
+
+ Returns:
+ bool: True if content range total bytes is zero, false otherwise.
+ """
+ if get_status_code(response) == http_client. \
+ REQUESTED_RANGE_NOT_SATISFIABLE:
+ content_range = _helpers.header_required(
+ response, _helpers.CONTENT_RANGE_HEADER,
+ get_headers, callback=_helpers.do_nothing)
+ if content_range == _ZERO_CONTENT_RANGE_HEADER:
+ return True
+ return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google/resumable_media/_upload.py
new/google-resumable-media-0.4.0/google/resumable_media/_upload.py
--- old/google-resumable-media-0.3.2/google/resumable_media/_upload.py
2018-12-20 08:07:56.000000000 +0100
+++ new/google-resumable-media-0.4.0/google/resumable_media/_upload.py
2019-09-06 18:48:30.000000000 +0200
@@ -319,7 +319,8 @@
def __init__(self, upload_url, chunk_size, headers=None):
super(ResumableUpload, self).__init__(upload_url, headers=headers)
if chunk_size % resumable_media.UPLOAD_CHUNK_SIZE != 0:
- raise ValueError(u'256 KB must divide chunk size')
+ raise ValueError(u'{} KB must divide chunk size'.format(
+ resumable_media.UPLOAD_CHUNK_SIZE / 1024))
self._chunk_size = chunk_size
self._stream = None
self._content_type = None
@@ -445,6 +446,12 @@
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
+ _helpers.require_status_code(
+ response,
+ (http_client.OK,),
+ self._get_status_code,
+ callback=self._make_invalid,
+ )
self._resumable_url = _helpers.header_required(
response, u'location', self._get_headers)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google/resumable_media/requests/__init__.py
new/google-resumable-media-0.4.0/google/resumable_media/requests/__init__.py
---
old/google-resumable-media-0.3.2/google/resumable_media/requests/__init__.py
2018-12-20 08:07:56.000000000 +0100
+++
new/google-resumable-media-0.4.0/google/resumable_media/requests/__init__.py
2019-09-06 18:48:30.000000000 +0200
@@ -658,20 +658,6 @@
>>> json_response[u'name'] == blob_name
True
"""
-
-import pkg_resources
-
-try:
- pkg_resources.require('requests >= 2.18.0')
-except pkg_resources.ResolutionError as caught_exc: # pragma: NO COVER
- import six
- new_exc = ImportError(
- '``requests >= 2.18.0`` is required by the '
- '``google.resumable_media.requests`` subpackage.\n'
- 'It can be installed via\n'
- ' pip install google-resumable-media[requests].')
- six.raise_from(new_exc, caught_exc)
-
from google.resumable_media.requests.download import ChunkedDownload
from google.resumable_media.requests.download import Download
from google.resumable_media.requests.upload import MultipartUpload
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google/resumable_media/requests/_helpers.py
new/google-resumable-media-0.4.0/google/resumable_media/requests/_helpers.py
---
old/google-resumable-media-0.3.2/google/resumable_media/requests/_helpers.py
2018-12-12 22:17:35.000000000 +0100
+++
new/google-resumable-media-0.4.0/google/resumable_media/requests/_helpers.py
2019-09-06 18:48:30.000000000 +0200
@@ -25,6 +25,13 @@
_DEFAULT_RETRY_STRATEGY = common.RetryStrategy()
+_SINGLE_GET_CHUNK_SIZE = 8192
+# The number of seconds to wait to establish a connection
+# (connect() call on socket). Avoid setting this to a multiple of 3 to not
+# Align with TCP Retransmission timing. (typically 2.5-3s)
+_DEFAULT_CONNECT_TIMEOUT = 61
+# The number of seconds to wait between bytes sent from the server.
+_DEFAULT_READ_TIMEOUT = 60
class RequestsMixin(object):
@@ -69,7 +76,12 @@
Returns:
bytes: The body of the ``response``.
"""
- return response.content
+ if response._content is False:
+ response._content = b''.join(
+ response.raw.stream(
+ _SINGLE_GET_CHUNK_SIZE, decode_content=False))
+ response._content_consumed = True
+ return response._content
def http_request(transport, method, url, data=None, headers=None,
@@ -94,6 +106,10 @@
Returns:
~requests.Response: The return value of ``transport.request()``.
"""
+ if "timeout" not in transport_kwargs:
+ transport_kwargs["timeout"] = (
+ _DEFAULT_CONNECT_TIMEOUT, _DEFAULT_READ_TIMEOUT)
+
func = functools.partial(
transport.request, method, url, data=data, headers=headers,
**transport_kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google/resumable_media/requests/download.py
new/google-resumable-media-0.4.0/google/resumable_media/requests/download.py
---
old/google-resumable-media-0.3.2/google/resumable_media/requests/download.py
2018-12-20 22:14:25.000000000 +0100
+++
new/google-resumable-media-0.4.0/google/resumable_media/requests/download.py
2019-09-06 18:48:30.000000000 +0200
@@ -18,15 +18,12 @@
import hashlib
import logging
-import urllib3.response
-
from google.resumable_media import _download
from google.resumable_media import common
from google.resumable_media.requests import _helpers
_LOGGER = logging.getLogger(__name__)
-_SINGLE_GET_CHUNK_SIZE = 8192
_HASH_HEADER = u'x-goog-hash'
_MISSING_MD5 = u"""\
No MD5 checksum was returned from the service while downloading {}
@@ -117,12 +114,12 @@
with response:
# NOTE: This might "donate" ``md5_hash`` to the decoder and replace
# it with a ``_DoNothingHash``.
- local_hash = _add_decoder(response.raw, md5_hash)
- body_iter = response.iter_content(
- chunk_size=_SINGLE_GET_CHUNK_SIZE, decode_unicode=False)
+ body_iter = response.raw.stream(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False)
for chunk in body_iter:
self._stream.write(chunk)
- local_hash.update(chunk)
+ md5_hash.update(chunk)
+ response._content_consumed = True
if expected_md5_hash is None:
return
@@ -157,23 +154,22 @@
"""
method, url, payload, headers = self._prepare_request()
# NOTE: We assume "payload is None" but pass it along anyway.
- request_kwargs = {
- u'data': payload,
- u'headers': headers,
- u'retry_strategy': self._retry_strategy,
- }
- if self._stream is not None:
- request_kwargs[u'stream'] = True
-
- result = _helpers.http_request(
- transport, method, url, **request_kwargs)
+ response = _helpers.http_request(
+ transport,
+ method,
+ url,
+ data=payload,
+ headers=headers,
+ retry_strategy=self._retry_strategy,
+ stream=True,
+ )
- self._process_response(result)
+ self._process_response(response)
if self._stream is not None:
- self._write_to_stream(result)
+ self._write_to_stream(response)
- return result
+ return response
class ChunkedDownload(_helpers.RequestsMixin, _download.ChunkedDownload):
@@ -219,11 +215,11 @@
"""
method, url, payload, headers = self._prepare_request()
# NOTE: We assume "payload is None" but pass it along anyway.
- result = _helpers.http_request(
+ response = _helpers.http_request(
transport, method, url, data=payload, headers=headers,
- retry_strategy=self._retry_strategy)
- self._process_response(result)
- return result
+ retry_strategy=self._retry_strategy, stream=True)
+ self._process_response(response)
+ return response
def _parse_md5_header(header_value, response):
@@ -291,58 +287,3 @@
Args:
unused_chunk (bytes): A chunk of data.
"""
-
-
-def _add_decoder(response_raw, md5_hash):
- """Patch the ``_decoder`` on a ``urllib3`` response.
-
- This is so that we can intercept the compressed bytes before they are
- decoded.
-
- Only patches if the content encoding is ``gzip``.
-
- Args:
- response_raw (urllib3.response.HTTPResponse): The raw response for
- an HTTP request.
- md5_hash (Union[_DoNothingHash, hashlib.md5]): A hash function which
- will get updated when it encounters compressed bytes.
-
- Returns:
- Union[_DoNothingHash, hashlib.md5]: Either the original ``md5_hash``
- if ``_decoder`` is not patched. Otherwise, returns a ``_DoNothingHash``
- since the caller will no longer need to hash to decoded bytes.
- """
- encoding = response_raw.headers.get(u'content-encoding', u'').lower()
- if encoding != u'gzip':
- return md5_hash
-
- response_raw._decoder = _GzipDecoder(md5_hash)
- return _DoNothingHash()
-
-
-class _GzipDecoder(urllib3.response.GzipDecoder):
- """Custom subclass of ``urllib3`` decoder for ``gzip``-ed bytes.
-
- Allows an MD5 hash function to see the compressed bytes before they are
- decoded. This way the hash of the compressed value can be computed.
-
- Args:
- md5_hash (Union[_DoNothingHash, hashlib.md5]): A hash function which
- will get updated when it encounters compressed bytes.
- """
-
- def __init__(self, md5_hash):
- super(_GzipDecoder, self).__init__()
- self._md5_hash = md5_hash
-
- def decompress(self, data):
- """Decompress the bytes.
-
- Args:
- data (bytes): The compressed bytes to be decompressed.
-
- Returns:
- bytes: The decompressed bytes from ``data``.
- """
- self._md5_hash.update(data)
- return super(_GzipDecoder, self).decompress(data)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google/resumable_media/requests/upload.py
new/google-resumable-media-0.4.0/google/resumable_media/requests/upload.py
--- old/google-resumable-media-0.3.2/google/resumable_media/requests/upload.py
2018-12-12 22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/google/resumable_media/requests/upload.py
2019-09-06 18:48:30.000000000 +0200
@@ -53,11 +53,11 @@
"""
method, url, payload, headers = self._prepare_request(
data, content_type)
- result = _helpers.http_request(
+ response = _helpers.http_request(
transport, method, url, data=payload, headers=headers,
retry_strategy=self._retry_strategy)
- self._process_response(result)
- return result
+ self._process_response(response)
+ return response
class MultipartUpload(_helpers.RequestsMixin, _upload.MultipartUpload):
@@ -92,11 +92,11 @@
"""
method, url, payload, headers = self._prepare_request(
data, metadata, content_type)
- result = _helpers.http_request(
+ response = _helpers.http_request(
transport, method, url, data=payload, headers=headers,
retry_strategy=self._retry_strategy)
- self._process_response(result)
- return result
+ self._process_response(response)
+ return response
class ResumableUpload(_helpers.RequestsMixin, _upload.ResumableUpload):
@@ -321,11 +321,11 @@
method, url, payload, headers = self._prepare_initiate_request(
stream, metadata, content_type,
total_bytes=total_bytes, stream_final=stream_final)
- result = _helpers.http_request(
+ response = _helpers.http_request(
transport, method, url, data=payload, headers=headers,
retry_strategy=self._retry_strategy)
- self._process_initiate_response(result)
- return result
+ self._process_initiate_response(response)
+ return response
def transmit_next_chunk(self, transport):
"""Transmit the next chunk of the resource to be uploaded.
@@ -390,11 +390,11 @@
code is not 200 or 308.
"""
method, url, payload, headers = self._prepare_request()
- result = _helpers.http_request(
+ response = _helpers.http_request(
transport, method, url, data=payload, headers=headers,
retry_strategy=self._retry_strategy)
- self._process_response(result, len(payload))
- return result
+ self._process_response(response, len(payload))
+ return response
def recover(self, transport):
"""Recover from a failure.
@@ -415,8 +415,8 @@
"""
method, url, payload, headers = self._prepare_recover_request()
# NOTE: We assume "payload is None" but pass it along anyway.
- result = _helpers.http_request(
+ response = _helpers.http_request(
transport, method, url, data=payload, headers=headers,
retry_strategy=self._retry_strategy)
- self._process_recover_response(result)
- return result
+ self._process_recover_response(response)
+ return response
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google_resumable_media.egg-info/PKG-INFO
new/google-resumable-media-0.4.0/google_resumable_media.egg-info/PKG-INFO
--- old/google-resumable-media-0.3.2/google_resumable_media.egg-info/PKG-INFO
2018-12-20 23:28:45.000000000 +0100
+++ new/google-resumable-media-0.4.0/google_resumable_media.egg-info/PKG-INFO
2019-09-06 18:49:51.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: google-resumable-media
-Version: 0.3.2
+Version: 0.4.0
Summary: Utilities for Google Media Downloads and Resumable Uploads
Home-page: https://github.com/GoogleCloudPlatform/google-resumable-media-python
Author: Google Cloud Platform
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/google_resumable_media.egg-info/SOURCES.txt
new/google-resumable-media-0.4.0/google_resumable_media.egg-info/SOURCES.txt
---
old/google-resumable-media-0.3.2/google_resumable_media.egg-info/SOURCES.txt
2018-12-20 23:28:47.000000000 +0100
+++
new/google-resumable-media-0.4.0/google_resumable_media.egg-info/SOURCES.txt
2019-09-06 18:49:51.000000000 +0200
@@ -20,9 +20,7 @@
google_resumable_media.egg-info/not-zip-safe
google_resumable_media.egg-info/requires.txt
google_resumable_media.egg-info/top_level.txt
-tests/.DS_Store
tests/__init__.py
-tests/data/.DS_Store
tests/data/favicon.ico
tests/data/file.txt
tests/data/gzipped.txt
@@ -33,6 +31,7 @@
tests/system/credentials.json.enc
tests/system/utils.py
tests/system/requests/__init__.py
+tests/system/requests/conftest.py
tests/system/requests/test_download.py
tests/system/requests/test_upload.py
tests/unit/__init__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-resumable-media-0.3.2/setup.py
new/google-resumable-media-0.4.0/setup.py
--- old/google-resumable-media-0.3.2/setup.py 2018-12-20 23:22:12.000000000
+0100
+++ new/google-resumable-media-0.4.0/setup.py 2019-09-06 18:48:30.000000000
+0200
@@ -34,7 +34,7 @@
setuptools.setup(
name='google-resumable-media',
- version='0.3.2',
+ version = '0.4.0',
description='Utilities for Google Media Downloads and Resumable Uploads',
author='Google Cloud Platform',
author_email='[email protected]',
Binary files old/google-resumable-media-0.3.2/tests/.DS_Store and
new/google-resumable-media-0.4.0/tests/.DS_Store differ
Binary files old/google-resumable-media-0.3.2/tests/data/.DS_Store and
new/google-resumable-media-0.4.0/tests/data/.DS_Store differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/system/requests/conftest.py
new/google-resumable-media-0.4.0/tests/system/requests/conftest.py
--- old/google-resumable-media-0.3.2/tests/system/requests/conftest.py
1970-01-01 01:00:00.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/system/requests/conftest.py
2019-09-06 18:48:30.000000000 +0200
@@ -0,0 +1,61 @@
+# Copyright 2019 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""py.test fixtures to be shared across multiple system test modules."""
+
+import google.auth
+import google.auth.transport.requests as tr_requests
+import pytest
+
+from tests.system import utils
+
+
+def ensure_bucket(transport):
+ get_response = transport.get(utils.BUCKET_URL)
+ if get_response.status_code == 404:
+ credentials = transport.credentials
+ query_params = {
+ 'project': credentials.project_id,
+ }
+ payload = {
+ 'name': utils.BUCKET_NAME,
+ }
+ post_response = transport.post(
+ utils.BUCKET_POST_URL, params=query_params, json=payload)
+
+ if not post_response.ok:
+ raise ValueError("{}: {}".format(
+ post_response.status_code, post_response.reason))
+
+
+def cleanup_bucket(transport):
+ del_response = transport.delete(utils.BUCKET_URL)
+
+ if not del_response.ok:
+ raise ValueError("{}: {}".format(
+ del_response.status_code, del_response.reason))
+
+
[email protected](scope=u'session')
+def authorized_transport():
+ credentials, _ = google.auth.default(scopes=(utils.GCS_RW_SCOPE,))
+ yield tr_requests.AuthorizedSession(credentials)
+
+
[email protected](scope=u'session')
+def bucket(authorized_transport):
+ ensure_bucket(authorized_transport)
+
+ yield utils.BUCKET_URL
+
+ cleanup_bucket(authorized_transport)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/system/requests/test_download.py
new/google-resumable-media-0.4.0/tests/system/requests/test_download.py
--- old/google-resumable-media-0.3.2/tests/system/requests/test_download.py
2018-12-20 23:22:12.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/system/requests/test_download.py
2019-09-06 18:48:30.000000000 +0200
@@ -25,8 +25,9 @@
from six.moves import http_client
from google import resumable_media
-import google.resumable_media.requests as resumable_requests
-import google.resumable_media.requests.download as download_mod
+from google.resumable_media import requests as resumable_requests
+from google.resumable_media.requests import download as download_mod
+from google.resumable_media.requests import _helpers
from tests.system import utils
@@ -58,11 +59,10 @@
}, {
u'path': os.path.realpath(os.path.join(DATA_DIR, u'file.txt')),
u'content_type': PLAIN_TEXT,
- u'checksum': u'KHRs/+ZSrc/FuuR4qz/PZQ==',
+ u'checksum': u'XHSHAr/SpIeZtZbjgQ4nGw==',
u'slices': (),
}, {
u'path': os.path.realpath(os.path.join(DATA_DIR, u'gzipped.txt.gz')),
- u'uncompressed': os.path.realpath(os.path.join(DATA_DIR,
u'gzipped.txt')),
u'content_type': PLAIN_TEXT,
u'checksum': u'KHRs/+ZSrc/FuuR4qz/PZQ==',
u'slices': (),
@@ -77,7 +77,7 @@
u'The content for this response was already consumed')
NOT_FOUND_ERR = (
b'No such object: ' +
- bytes(os.environ['GOOGLE_RESUMABLE_MEDIA_BUCKET'], 'utf-8') +
+ utils.BUCKET_NAME.encode('utf-8') +
b'/does-not-exist.txt'
)
@@ -111,12 +111,6 @@
return response
[email protected](scope=u'module')
-def authorized_transport():
- credentials, _ = google.auth.default(scopes=(utils.GCS_RW_SCOPE,))
- yield tr_requests.AuthorizedSession(credentials)
-
-
# Transport that returns corrupt data, so we can exercise checksum handling.
@pytest.fixture(scope=u'module')
def corrupting_transport():
@@ -136,18 +130,18 @@
def _get_contents(info):
- full_path = info.get(u'uncompressed', info[u'path'])
+ full_path = info[u'path']
with open(full_path, u'rb') as file_obj:
return file_obj.read()
def _get_blob_name(info):
- full_path = info.get(u'uncompressed', info[u'path'])
+ full_path = info[u'path']
return os.path.basename(full_path)
@pytest.fixture(scope=u'module')
-def add_files(authorized_transport):
+def add_files(authorized_transport, bucket):
blob_names = []
for info in ALL_FILES:
to_upload = _get_contents_for_upload(info)
@@ -189,6 +183,11 @@
assert exc_info.match(u'Download has finished.')
+def read_raw_content(response):
+ return b''.join(response.raw.stream(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False))
+
+
def test_download_full(add_files, authorized_transport):
for info in ALL_FILES:
actual_contents = _get_contents(info)
@@ -200,7 +199,7 @@
# Consume the resource.
response = download.consume(authorized_transport)
assert response.status_code == http_client.OK
- assert response.content == actual_contents
+ assert read_raw_content(response) == actual_contents
check_tombstoned(download, authorized_transport)
@@ -245,7 +244,7 @@
@pytest.fixture(scope=u'module')
-def secret_file(authorized_transport):
+def secret_file(authorized_transport, bucket):
blob_name = u'super-seekrit.txt'
data = b'Please do not tell anyone my encrypted seekrit.'
@@ -290,7 +289,7 @@
check_tombstoned(download_wo, authorized_transport)
-def test_non_existent_file(authorized_transport):
+def test_non_existent_file(authorized_transport, bucket):
blob_name = u'does-not-exist.txt'
media_url = utils.DOWNLOAD_URL_TEMPLATE.format(blob_name=blob_name)
download = resumable_requests.Download(media_url)
@@ -303,7 +302,7 @@
@pytest.fixture(scope=u'module')
-def simple_file(authorized_transport):
+def simple_file(authorized_transport, bucket):
blob_name = u'basic-file.txt'
upload_url = utils.SIMPLE_UPLOAD_TEMPLATE.format(blob_name=blob_name)
upload = resumable_requests.SimpleUpload(upload_url)
@@ -398,7 +397,7 @@
return num_responses, response
-def test_chunked_download(add_files, authorized_transport):
+def test_chunked_download_full(add_files, authorized_transport):
for info in ALL_FILES:
actual_contents = _get_contents(info)
blob_name = _get_blob_name(info)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/system/requests/test_upload.py
new/google-resumable-media-0.4.0/tests/system/requests/test_upload.py
--- old/google-resumable-media-0.3.2/tests/system/requests/test_upload.py
2018-12-12 22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/system/requests/test_upload.py
2019-09-06 18:48:30.000000000 +0200
@@ -17,8 +17,6 @@
import io
import os
-import google.auth
-import google.auth.transport.requests as tr_requests
import pytest
from six.moves import http_client
from six.moves import urllib_parse
@@ -42,12 +40,6 @@
b'1024 bytes, which does not meet this requirement.')
[email protected](scope=u'module')
-def authorized_transport():
- credentials, _ = google.auth.default(scopes=(utils.GCS_RW_SCOPE,))
- yield tr_requests.AuthorizedSession(credentials)
-
-
@pytest.fixture
def cleanup():
to_delete = []
@@ -116,7 +108,7 @@
assert response.status_code == http_client.NOT_FOUND
-def test_simple_upload(authorized_transport, cleanup):
+def test_simple_upload(authorized_transport, bucket, cleanup):
with open(ICO_FILE, u'rb') as file_obj:
actual_contents = file_obj.read()
@@ -139,7 +131,7 @@
upload, authorized_transport, actual_contents, ICO_CONTENT_TYPE)
-def test_simple_upload_with_headers(authorized_transport, cleanup):
+def test_simple_upload_with_headers(authorized_transport, bucket, cleanup):
blob_name = u'some-stuff.bin'
# Make sure to clean up the uploaded blob when we are done.
cleanup(blob_name, authorized_transport)
@@ -163,7 +155,7 @@
upload, authorized_transport, data, BYTES_CONTENT_TYPE)
-def test_multipart_upload(authorized_transport, cleanup):
+def test_multipart_upload(authorized_transport, bucket, cleanup):
with open(ICO_FILE, u'rb') as file_obj:
actual_contents = file_obj.read()
@@ -193,7 +185,7 @@
metadata, ICO_CONTENT_TYPE)
-def test_multipart_upload_with_headers(authorized_transport, cleanup):
+def test_multipart_upload_with_headers(authorized_transport, bucket, cleanup):
blob_name = u'some-multipart-stuff.bin'
# Make sure to clean up the uploaded blob when we are done.
cleanup(blob_name, authorized_transport)
@@ -308,12 +300,12 @@
check_tombstoned(upload, authorized_transport)
-def test_resumable_upload(authorized_transport, img_stream, cleanup):
+def test_resumable_upload(authorized_transport, img_stream, bucket, cleanup):
_resumable_upload_helper(authorized_transport, img_stream, cleanup)
def test_resumable_upload_with_headers(
- authorized_transport, img_stream, cleanup):
+ authorized_transport, img_stream, bucket, cleanup):
headers = utils.get_encryption_headers()
_resumable_upload_helper(
authorized_transport, img_stream, cleanup, headers=headers)
@@ -405,11 +397,12 @@
check_tombstoned(upload, authorized_transport)
-def test_resumable_upload_recover(authorized_transport, cleanup):
+def test_resumable_upload_recover(authorized_transport, bucket, cleanup):
_resumable_upload_recover_helper(authorized_transport, cleanup)
-def test_resumable_upload_recover_with_headers(authorized_transport, cleanup):
+def test_resumable_upload_recover_with_headers(
+ authorized_transport, bucket, cleanup):
headers = utils.get_encryption_headers()
_resumable_upload_recover_helper(
authorized_transport, cleanup, headers=headers)
@@ -445,7 +438,8 @@
self._check_range_sent(response, start_byte, end_byte, u'*')
self._check_range_received(response, end_byte + 1)
- def test_smaller_than_chunk_size(self, authorized_transport, cleanup):
+ def test_smaller_than_chunk_size(
+ self, authorized_transport, bucket, cleanup):
blob_name = os.path.basename(ICO_FILE)
chunk_size = resumable_media.UPLOAD_CHUNK_SIZE
# Make sure to clean up the uploaded blob when we are done.
@@ -480,7 +474,7 @@
# Make sure the upload is tombstoned.
check_tombstoned(upload, authorized_transport)
- def test_finish_at_chunk(self, authorized_transport, cleanup):
+ def test_finish_at_chunk(self, authorized_transport, bucket, cleanup):
blob_name = u'some-clean-stuff.bin'
chunk_size = resumable_media.UPLOAD_CHUNK_SIZE
# Make sure to clean up the uploaded blob when we are done.
@@ -526,7 +520,7 @@
# Go back to where we were before the write.
stream.seek(curr_pos)
- def test_interleave_writes(self, authorized_transport, cleanup):
+ def test_interleave_writes(self, authorized_transport, bucket, cleanup):
blob_name = u'some-moar-stuff.bin'
chunk_size = resumable_media.UPLOAD_CHUNK_SIZE
# Make sure to clean up the uploaded blob when we are done.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-resumable-media-0.3.2/tests/system/utils.py
new/google-resumable-media-0.4.0/tests/system/utils.py
--- old/google-resumable-media-0.3.2/tests/system/utils.py 2018-12-12
22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/system/utils.py 2019-09-06
18:48:30.000000000 +0200
@@ -14,25 +14,24 @@
import base64
import hashlib
-import os
+import time
-BUCKET_NAME = os.environ[u'GOOGLE_RESUMABLE_MEDIA_BUCKET']
-DOWNLOAD_URL_TEMPLATE = (
- u'https://www.googleapis.com/download/storage/v1/b/' +
- BUCKET_NAME +
- u'/o/{blob_name}?alt=media')
-_UPLOAD_BASE = (
- u'https://www.googleapis.com/upload/storage/v1/b/' +
- BUCKET_NAME +
- u'/o?uploadType=')
+BUCKET_NAME = u'grpm-systest-{}'.format(int(1000 * time.time()))
+BUCKET_POST_URL = u'https://www.googleapis.com/storage/v1/b/'
+BUCKET_URL = u'https://www.googleapis.com/storage/v1/b/{}'.format(BUCKET_NAME)
+
+_DOWNLOAD_BASE = u'https://www.googleapis.com/download/storage/v1/b/{}'.format(
+ BUCKET_NAME)
+DOWNLOAD_URL_TEMPLATE = _DOWNLOAD_BASE + u'/o/{blob_name}?alt=media'
+
+_UPLOAD_BASE = u'https://www.googleapis.com/upload/storage/v1/b/{}'.format(
+ BUCKET_NAME) + u'/o?uploadType='
SIMPLE_UPLOAD_TEMPLATE = _UPLOAD_BASE + u'media&name={blob_name}'
MULTIPART_UPLOAD = _UPLOAD_BASE + u'multipart'
RESUMABLE_UPLOAD = _UPLOAD_BASE + u'resumable'
-METADATA_URL_TEMPLATE = (
- u'https://www.googleapis.com/storage/v1/b/' +
- BUCKET_NAME +
- u'/o/{blob_name}')
+
+METADATA_URL_TEMPLATE = BUCKET_URL + u'/o/{blob_name}'
GCS_RW_SCOPE = u'https://www.googleapis.com/auth/devstorage.read_write'
# Generated using random.choice() with all 256 byte choices.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/unit/requests/test__helpers.py
new/google-resumable-media-0.4.0/tests/unit/requests/test__helpers.py
--- old/google-resumable-media-0.3.2/tests/unit/requests/test__helpers.py
2018-12-12 22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/unit/requests/test__helpers.py
2019-09-06 18:48:30.000000000 +0200
@@ -17,6 +17,8 @@
from google.resumable_media.requests import _helpers
+EXPECTED_TIMEOUT = (61, 60)
+
class TestRequestsMixin(object):
@@ -27,12 +29,21 @@
def test__get_headers(self):
headers = {u'fruit': u'apple'}
- response = mock.Mock(headers=headers, spec=[u'headers'])
+ response = mock.Mock(headers=headers, spec=['headers'])
assert headers == _helpers.RequestsMixin._get_headers(response)
- def test__get_body(self):
+ def test__get_body_wo_content_consumed(self):
+ body = b'This is the payload.'
+ raw = mock.Mock(spec=['stream'])
+ raw.stream.return_value = iter([body])
+ response = mock.Mock(raw=raw, _content=False, spec=['raw', '_content'])
+ assert body == _helpers.RequestsMixin._get_body(response)
+ raw.stream.assert_called_once_with(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False)
+
+ def test__get_body_w_content_consumed(self):
body = b'This is the payload.'
- response = mock.Mock(content=body, spec=[u'content'])
+ response = mock.Mock(_content=body, spec=['_content'])
assert body == _helpers.RequestsMixin._get_body(response)
@@ -42,14 +53,27 @@
url = u'http://test.invalid'
data = mock.sentinel.data
headers = {u'one': u'fish', u'blue': u'fish'}
+ timeout = mock.sentinel.timeout
ret_val = _helpers.http_request(
transport, method, url, data=data, headers=headers,
- extra1=b'work', extra2=125.5)
+ extra1=b'work', extra2=125.5, timeout=timeout)
assert ret_val is responses[0]
transport.request.assert_called_once_with(
method, url, data=data, headers=headers,
- extra1=b'work', extra2=125.5)
+ extra1=b'work', extra2=125.5, timeout=timeout)
+
+
+def test_http_request_defaults():
+ transport, responses = _make_transport(http_client.OK)
+ method = u'POST'
+ url = u'http://test.invalid'
+ ret_val = _helpers.http_request(transport, method, url)
+
+ assert ret_val is responses[0]
+ transport.request.assert_called_once_with(
+ method, url, data=None, headers=None,
+ timeout=EXPECTED_TIMEOUT)
def _make_response(status_code):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/unit/requests/test_download.py
new/google-resumable-media-0.4.0/tests/unit/requests/test_download.py
--- old/google-resumable-media-0.3.2/tests/unit/requests/test_download.py
2018-12-12 22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/unit/requests/test_download.py
2019-09-06 18:48:30.000000000 +0200
@@ -19,17 +19,19 @@
from six.moves import http_client
from google.resumable_media import common
-import google.resumable_media.requests.download as download_mod
+from google.resumable_media.requests import download as download_mod
+from google.resumable_media.requests import _helpers
EXAMPLE_URL = (
u'https://www.googleapis.com/download/storage/v1/b/'
u'{BUCKET}/o/{OBJECT}?alt=media')
+EXPECTED_TIMEOUT = (61, 60)
class TestDownload(object):
- @mock.patch(u'google.resumable_media.requests.download._LOGGER')
+ @mock.patch('google.resumable_media.requests.download._LOGGER')
def test__get_expected_md5_present(self, _LOGGER):
download = download_mod.Download(EXAMPLE_URL)
@@ -42,7 +44,7 @@
assert expected_md5_hash == checksum
_LOGGER.info.assert_not_called()
- @mock.patch(u'google.resumable_media.requests.download._LOGGER')
+ @mock.patch('google.resumable_media.requests.download._LOGGER')
def test__get_expected_md5_missing(self, _LOGGER):
download = download_mod.Download(EXAMPLE_URL)
@@ -70,9 +72,8 @@
# Check mocks.
response.__enter__.assert_called_once_with()
response.__exit__.assert_called_once_with(None, None, None)
- response.iter_content.assert_called_once_with(
- chunk_size=download_mod._SINGLE_GET_CHUNK_SIZE,
- decode_unicode=False)
+ response.raw.stream.assert_called_once_with(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False)
def test__write_to_stream_with_hash_check_success(self):
stream = io.BytesIO()
@@ -94,9 +95,8 @@
# Check mocks.
response.__enter__.assert_called_once_with()
response.__exit__.assert_called_once_with(None, None, None)
- response.iter_content.assert_called_once_with(
- chunk_size=download_mod._SINGLE_GET_CHUNK_SIZE,
- decode_unicode=False)
+ response.raw.stream.assert_called_once_with(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False)
def test__write_to_stream_with_hash_check_fail(self):
stream = io.BytesIO()
@@ -127,16 +127,15 @@
# Check mocks.
response.__enter__.assert_called_once_with()
response.__exit__.assert_called_once_with(None, None, None)
- response.iter_content.assert_called_once_with(
- chunk_size=download_mod._SINGLE_GET_CHUNK_SIZE,
- decode_unicode=False)
+ response.raw.stream.assert_called_once_with(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False)
def _consume_helper(
self, stream=None, end=65536, headers=None, chunks=(),
response_headers=None):
download = download_mod.Download(
EXAMPLE_URL, stream=stream, end=end, headers=headers)
- transport = mock.Mock(spec=[u'request'])
+ transport = mock.Mock(spec=['request'])
transport.request.return_value = _mock_response(
chunks=chunks, headers=response_headers)
@@ -144,12 +143,17 @@
ret_val = download.consume(transport)
assert ret_val is transport.request.return_value
- called_kwargs = {u'data': None, u'headers': download._headers}
if chunks:
assert stream is not None
- called_kwargs[u'stream'] = True
+
transport.request.assert_called_once_with(
- u'GET', EXAMPLE_URL, **called_kwargs)
+ u'GET',
+ EXAMPLE_URL,
+ data=None,
+ headers=download._headers,
+ stream=True,
+ timeout=EXPECTED_TIMEOUT,
+ )
range_bytes = u'bytes={:d}-{:d}'.format(0, end)
assert download._headers[u'range'] == range_bytes
@@ -171,9 +175,8 @@
response = transport.request.return_value
response.__enter__.assert_called_once_with()
response.__exit__.assert_called_once_with(None, None, None)
- response.iter_content.assert_called_once_with(
- chunk_size=download_mod._SINGLE_GET_CHUNK_SIZE,
- decode_unicode=False)
+ response.raw.stream.assert_called_once_with(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False)
def test_consume_with_stream_hash_check_success(self):
stream = io.BytesIO()
@@ -189,9 +192,8 @@
response = transport.request.return_value
response.__enter__.assert_called_once_with()
response.__exit__.assert_called_once_with(None, None, None)
- response.iter_content.assert_called_once_with(
- chunk_size=download_mod._SINGLE_GET_CHUNK_SIZE,
- decode_unicode=False)
+ response.raw.stream.assert_called_once_with(
+ _helpers._SINGLE_GET_CHUNK_SIZE, decode_content=False)
def test_consume_with_stream_hash_check_fail(self):
stream = io.BytesIO()
@@ -202,7 +204,7 @@
bad_checksum = u'anVzdCBub3QgdGhpcyAxLA=='
header_value = u'crc32c=V0FUPw==,md5={}'.format(bad_checksum)
headers = {download_mod._HASH_HEADER: header_value}
- transport = mock.Mock(spec=[u'request'])
+ transport = mock.Mock(spec=['request'])
transport.request.return_value = _mock_response(
chunks=chunks, headers=headers)
@@ -224,7 +226,8 @@
# Check mocks.
transport.request.assert_called_once_with(
- u'GET', EXAMPLE_URL, data=None, headers={}, stream=True)
+ u'GET', EXAMPLE_URL, data=None, headers={}, stream=True,
+ timeout=EXPECTED_TIMEOUT)
def test_consume_with_headers(self):
headers = {} # Empty headers
@@ -256,11 +259,11 @@
response_headers = self._response_headers(
start_byte, end_byte, total_bytes)
return mock.Mock(
- content=content,
+ _content=content,
headers=response_headers,
status_code=status_code,
spec=[
- u'content',
+ u'_content',
u'headers',
u'status_code',
],
@@ -273,7 +276,7 @@
download.consume_next_chunk(None)
def _mock_transport(self, start, chunk_size, total_bytes, content=b''):
- transport = mock.Mock(spec=[u'request'])
+ transport = mock.Mock(spec=['request'])
assert len(content) == chunk_size
transport.request.return_value = self._mock_response(
start, start + chunk_size - 1, total_bytes,
@@ -302,7 +305,13 @@
range_bytes = u'bytes={:d}-{:d}'.format(start, start + chunk_size - 1)
download_headers = {u'range': range_bytes}
transport.request.assert_called_once_with(
- u'GET', EXAMPLE_URL, data=None, headers=download_headers)
+ u'GET',
+ EXAMPLE_URL,
+ data=None,
+ headers=download_headers,
+ stream=True,
+ timeout=EXPECTED_TIMEOUT,
+ )
assert stream.getvalue() == data
# Go back and check the internal state after consuming the chunk.
assert not download.finished
@@ -362,75 +371,36 @@
assert return_value is None
-class Test__add_decoder(object):
-
- def test_non_gzipped(self):
- response_raw = mock.Mock(headers={}, spec=[u'headers'])
- md5_hash = download_mod._add_decoder(
- response_raw, mock.sentinel.md5_hash)
-
- assert md5_hash is mock.sentinel.md5_hash
-
- def test_gzipped(self):
- headers = {u'content-encoding': u'gzip'}
- response_raw = mock.Mock(
- headers=headers, spec=[u'headers', u'_decoder'])
- md5_hash = download_mod._add_decoder(
- response_raw, mock.sentinel.md5_hash)
-
- assert md5_hash is not mock.sentinel.md5_hash
- assert isinstance(md5_hash, download_mod._DoNothingHash)
- assert isinstance(response_raw._decoder, download_mod._GzipDecoder)
- assert response_raw._decoder._md5_hash is mock.sentinel.md5_hash
-
-
-class Test_GzipDecoder(object):
-
- def test_constructor(self):
- decoder = download_mod._GzipDecoder(mock.sentinel.md5_hash)
- assert decoder._md5_hash is mock.sentinel.md5_hash
-
- def test_decompress(self):
- md5_hash = mock.Mock(spec=['update'])
- decoder = download_mod._GzipDecoder(md5_hash)
-
- data = b'\x1f\x8b\x08\x08'
- result = decoder.decompress(data)
-
- assert result == b''
- md5_hash.update.assert_called_once_with(data)
-
-
def _mock_response(status_code=http_client.OK, chunks=(), headers=None):
if headers is None:
headers = {}
if chunks:
- mock_raw = mock.Mock(headers=headers, spec=[u'headers'])
+ mock_raw = mock.Mock(headers=headers, spec=['headers', 'stream'])
+ mock_raw.stream.return_value = iter(chunks)
response = mock.MagicMock(
headers=headers,
status_code=int(status_code),
raw=mock_raw,
spec=[
- u'__enter__',
- u'__exit__',
- u'iter_content',
- u'status_code',
- u'headers',
- u'raw',
+ '__enter__',
+ '__exit__',
+ 'raw',
+ 'status_code',
+ 'headers',
+ 'raw',
],
)
# i.e. context manager returns ``self``.
response.__enter__.return_value = response
response.__exit__.return_value = None
- response.iter_content.return_value = iter(chunks)
return response
else:
return mock.Mock(
headers=headers,
status_code=int(status_code),
spec=[
- u'status_code',
- u'headers',
+ 'status_code',
+ 'headers',
],
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/unit/requests/test_upload.py
new/google-resumable-media-0.4.0/tests/unit/requests/test_upload.py
--- old/google-resumable-media-0.3.2/tests/unit/requests/test_upload.py
2018-12-12 22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/unit/requests/test_upload.py
2019-09-06 18:48:30.000000000 +0200
@@ -35,6 +35,7 @@
BASIC_CONTENT = u'text/plain'
JSON_TYPE = u'application/json; charset=UTF-8'
JSON_TYPE_LINE = b'content-type: application/json; charset=UTF-8\r\n'
+EXPECTED_TIMEOUT = (61, 60)
class TestSimpleUpload(object):
@@ -51,7 +52,8 @@
assert ret_val is transport.request.return_value
upload_headers = {u'content-type': content_type}
transport.request.assert_called_once_with(
- u'POST', SIMPLE_URL, data=data, headers=upload_headers)
+ u'POST', SIMPLE_URL, data=data, headers=upload_headers,
+ timeout=EXPECTED_TIMEOUT)
assert upload.finished
@@ -84,7 +86,7 @@
upload_headers = {u'content-type': multipart_type}
transport.request.assert_called_once_with(
u'POST', MULTIPART_URL, data=expected_payload,
- headers=upload_headers)
+ headers=upload_headers, timeout=EXPECTED_TIMEOUT)
assert upload.finished
mock_get_boundary.assert_called_once_with()
@@ -121,7 +123,8 @@
u'x-upload-content-length': u'{:d}'.format(total_bytes),
}
transport.request.assert_called_once_with(
- u'POST', RESUMABLE_URL, data=json_bytes, headers=expected_headers)
+ u'POST', RESUMABLE_URL, data=json_bytes, headers=expected_headers,
+ timeout=EXPECTED_TIMEOUT)
@staticmethod
def _upload_in_flight(data, headers=None):
@@ -170,7 +173,7 @@
}
transport.request.assert_called_once_with(
u'PUT', upload.resumable_url, data=payload,
- headers=expected_headers)
+ headers=expected_headers, timeout=EXPECTED_TIMEOUT)
def test_recover(self):
upload = upload_mod.ResumableUpload(RESUMABLE_URL, ONE_MB)
@@ -191,7 +194,8 @@
upload._stream.seek.assert_called_once_with(end + 1)
expected_headers = {u'content-range': u'bytes */*'}
transport.request.assert_called_once_with(
- u'PUT', upload.resumable_url, data=None, headers=expected_headers)
+ u'PUT', upload.resumable_url, data=None, headers=expected_headers,
+ timeout=EXPECTED_TIMEOUT)
def _make_response(status_code=http_client.OK, headers=None):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/unit/test__download.py
new/google-resumable-media-0.4.0/tests/unit/test__download.py
--- old/google-resumable-media-0.3.2/tests/unit/test__download.py
2018-12-12 22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/unit/test__download.py
2019-09-06 18:48:30.000000000 +0200
@@ -127,7 +127,7 @@
# Make sure **not finished** before.
assert not download.finished
response = mock.Mock(
- status_code=int(http_client.OK), spec=[u'status_code'])
+ status_code=int(http_client.OK), spec=['status_code'])
ret_val = download._process_response(response)
assert ret_val is None
# Make sure **finished** after.
@@ -140,7 +140,7 @@
# Make sure **not finished** before.
assert not download.finished
response = mock.Mock(
- status_code=int(http_client.NOT_FOUND), spec=[u'status_code'])
+ status_code=int(http_client.NOT_FOUND), spec=['status_code'])
with pytest.raises(common.InvalidResponse) as exc_info:
download._process_response(response)
@@ -262,7 +262,7 @@
start_byte, end_byte, total_bytes)
return mock.Mock(
content=content, headers=response_headers, status_code=status_code,
- spec=[u'content', u'headers', u'status_code'])
+ spec=['content', 'headers', 'status_code'])
def test__prepare_request_already_finished(self):
download = _download.ChunkedDownload(EXAMPLE_URL, 64, None)
@@ -318,7 +318,8 @@
assert download.invalid
def test__process_response(self):
- chunk_size = 333
+ data = b'1234xyztL' * 37
+ chunk_size = len(data)
stream = io.BytesIO()
download = _download.ChunkedDownload(
EXAMPLE_URL, chunk_size, stream)
@@ -333,7 +334,6 @@
assert download.bytes_downloaded == already
assert download.total_bytes is None
# Actually call the method to update.
- data = b'1234xyztL' * 37 # 9 * 37 == 33
response = self._mock_response(
already, already + chunk_size - 1, total_bytes, content=data,
status_code=int(http_client.PARTIAL_CONTENT))
@@ -344,9 +344,39 @@
assert download.total_bytes == total_bytes
assert stream.getvalue() == data
+ def test__process_response_transfer_encoding(self):
+ data = b'1234xyztL' * 37
+ chunk_size = len(data)
+ stream = io.BytesIO()
+ download = _download.ChunkedDownload(
+ EXAMPLE_URL, chunk_size, stream)
+ _fix_up_virtual(download)
+
+ already = 22
+ download._bytes_downloaded = already
+ total_bytes = 4444
+
+ # Check internal state before.
+ assert not download.finished
+ assert download.bytes_downloaded == already
+ assert download.total_bytes is None
+ assert not download.invalid
+ # Actually call the method to update.
+ response = self._mock_response(
+ already, already + chunk_size - 1, total_bytes, content=data,
+ status_code=int(http_client.PARTIAL_CONTENT))
+ response.headers[u'transfer-encoding'] = 'chunked'
+ del response.headers[u'content-length']
+ download._process_response(response)
+ # Check internal state after.
+ assert not download.finished
+ assert download.bytes_downloaded == already + chunk_size
+ assert download.total_bytes == total_bytes
+ assert stream.getvalue() == data
+
def test__process_response_bad_status(self):
chunk_size = 384
- stream = mock.Mock(spec=[u'write'])
+ stream = mock.Mock(spec=['write'])
download = _download.ChunkedDownload(
EXAMPLE_URL, chunk_size, stream)
_fix_up_virtual(download)
@@ -387,9 +417,15 @@
assert download.total_bytes is None
assert not download.invalid
# Actually call the method to update.
+ headers = {
+ u'content-range': u'bytes 0-99/99',
+ }
response = mock.Mock(
- headers={}, status_code=int(http_client.PARTIAL_CONTENT),
- spec=[u'headers', u'status_code'])
+ headers=headers,
+ status_code=int(http_client.PARTIAL_CONTENT),
+ content=b'DEADBEEF',
+ spec=['headers', 'status_code', 'content'],
+ )
with pytest.raises(common.InvalidResponse) as exc_info:
download._process_response(response)
@@ -421,7 +457,7 @@
response = mock.Mock(
content=data, headers=headers,
status_code=int(http_client.PARTIAL_CONTENT),
- spec=[u'content', u'headers', u'status_code'])
+ spec=['content', 'headers', 'status_code'])
with pytest.raises(common.InvalidResponse) as exc_info:
download._process_response(response)
@@ -437,7 +473,7 @@
def test__process_response_body_wrong_length(self):
chunk_size = 10
- stream = mock.Mock(spec=[u'write'])
+ stream = mock.Mock(spec=['write'])
download = _download.ChunkedDownload(
EXAMPLE_URL, chunk_size, stream)
_fix_up_virtual(download)
@@ -523,6 +559,25 @@
assert download.total_bytes == 8 * chunk_size
assert stream.getvalue() == data
+ def test__process_response_when_content_range_is_zero(self):
+ chunk_size = 10
+ stream = mock.Mock(spec=[u'write'])
+ download = _download.ChunkedDownload(
+ EXAMPLE_URL, chunk_size, stream)
+ _fix_up_virtual(download)
+
+ content_range = _download._ZERO_CONTENT_RANGE_HEADER
+ headers = {u'content-range': content_range}
+ status_code = http_client.REQUESTED_RANGE_NOT_SATISFIABLE
+ response = mock.Mock(headers=headers,
+ status_code=status_code,
+ spec=[u'headers', 'status_code'])
+ download._process_response(response)
+ stream.write.assert_not_called()
+ assert download.finished
+ assert download.bytes_downloaded == 0
+ assert download.total_bytes is None
+
def test_consume_next_chunk(self):
download = _download.ChunkedDownload(EXAMPLE_URL, 256, None)
with pytest.raises(NotImplementedError) as exc_info:
@@ -569,7 +624,7 @@
@staticmethod
def _make_response(content_range):
headers = {u'content-range': content_range}
- return mock.Mock(headers=headers, spec=[u'headers'])
+ return mock.Mock(headers=headers, spec=['headers'])
def _success_helper(self, **kwargs):
content_range = u'Bytes 7-11/42'
@@ -608,7 +663,7 @@
callback.assert_called_once_with()
def _missing_header_helper(self, **kwargs):
- response = mock.Mock(headers={}, spec=[u'headers'])
+ response = mock.Mock(headers={}, spec=['headers'])
with pytest.raises(common.InvalidResponse) as exc_info:
_download.get_range_info(response, _get_headers, **kwargs)
@@ -626,6 +681,37 @@
callback.assert_called_once_with()
+class Test__check_for_zero_content_range(object):
+
+ @staticmethod
+ def _make_response(content_range, status_code):
+ headers = {u'content-range': content_range}
+ return mock.Mock(headers=headers,
+ status_code=status_code,
+ spec=[u'headers', 'status_code'])
+
+ def test_status_code_416_and_test_content_range_zero_both(self):
+ content_range = _download._ZERO_CONTENT_RANGE_HEADER
+ status_code = http_client.REQUESTED_RANGE_NOT_SATISFIABLE
+ response = self._make_response(content_range, status_code)
+ assert _download._check_for_zero_content_range(
+ response, _get_status_code, _get_headers)
+
+ def test_status_code_416_only(self):
+ content_range = u'bytes 2-5/3'
+ status_code = http_client.REQUESTED_RANGE_NOT_SATISFIABLE
+ response = self._make_response(content_range, status_code)
+ assert not _download._check_for_zero_content_range(
+ response, _get_status_code, _get_headers)
+
+ def test_content_range_zero_only(self):
+ content_range = _download._ZERO_CONTENT_RANGE_HEADER
+ status_code = http_client.OK
+ response = self._make_response(content_range, status_code)
+ assert not _download._check_for_zero_content_range(
+ response, _get_status_code, _get_headers)
+
+
def _get_status_code(response):
return response.status_code
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-0.3.2/tests/unit/test__upload.py
new/google-resumable-media-0.4.0/tests/unit/test__upload.py
--- old/google-resumable-media-0.3.2/tests/unit/test__upload.py 2018-12-12
22:17:35.000000000 +0100
+++ new/google-resumable-media-0.4.0/tests/unit/test__upload.py 2019-09-06
18:48:30.000000000 +0200
@@ -423,25 +423,26 @@
with pytest.raises(AttributeError):
upload._prepare_initiate_request(None, {}, BASIC_CONTENT)
- def test__process_initiate_response_bad_response(self):
+ def test__process_initiate_response_non_200(self):
upload = _upload.ResumableUpload(RESUMABLE_URL, ONE_MB)
_fix_up_virtual(upload)
- response = _make_response()
+ response = _make_response(403)
with pytest.raises(common.InvalidResponse) as exc_info:
upload._process_initiate_response(response)
error = exc_info.value
assert error.response is response
- assert len(error.args) == 2
- assert error.args[1] == u'location'
+ assert len(error.args) == 4
+ assert error.args[1] == 403
+ assert error.args[3] == 200
def test__process_initiate_response(self):
upload = _upload.ResumableUpload(RESUMABLE_URL, ONE_MB)
_fix_up_virtual(upload)
headers = {u'location': u'http://test.invalid?upload_id=kmfeij3234'}
- response = mock.Mock(headers=headers, spec=[u'headers'])
+ response = _make_response(headers=headers)
# Check resumable_url before.
assert upload._resumable_url is None
# Process the actual headers.