Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-google-resumable-media for
openSUSE:Factory checked in at 2022-01-23 18:38:42
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-google-resumable-media (Old)
and /work/SRC/openSUSE:Factory/.python-google-resumable-media.new.1938
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-google-resumable-media"
Sun Jan 23 18:38:42 2022 rev:12 rq:948235 version:2.1.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-google-resumable-media/python-google-resumable-media.changes
2021-09-30 23:45:00.992580751 +0200
+++
/work/SRC/openSUSE:Factory/.python-google-resumable-media.new.1938/python-google-resumable-media.changes
2022-01-23 18:38:44.353926393 +0100
@@ -1,0 +2,7 @@
+Sun Jan 23 15:54:35 UTC 2022 - Dirk M??ller <[email protected]>
+
+- update to 2.1.0:
+ * add support for Python 3.10
+ * Include ConnectionError and urllib3 exception as retriable
+
+-------------------------------------------------------------------
Old:
----
google-resumable-media-2.0.0.tar.gz
New:
----
google-resumable-media-2.1.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-google-resumable-media.spec ++++++
--- /var/tmp/diff_new_pack.wqDn2m/_old 2022-01-23 18:38:44.957922328 +0100
+++ /var/tmp/diff_new_pack.wqDn2m/_new 2022-01-23 18:38:44.961922302 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-google-resumable-media
#
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-google-resumable-media
-Version: 2.0.0
+Version: 2.1.0
Release: 0
Summary: Utilities for Google Media Downloads and Resumable Uploads
License: Apache-2.0
++++++ google-resumable-media-2.0.0.tar.gz ->
google-resumable-media-2.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-resumable-media-2.0.0/PKG-INFO
new/google-resumable-media-2.1.0/PKG-INFO
--- old/google-resumable-media-2.0.0/PKG-INFO 2021-08-19 22:13:53.599198000
+0200
+++ new/google-resumable-media-2.1.0/PKG-INFO 2021-10-25 19:36:02.955088000
+0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: google-resumable-media
-Version: 2.0.0
+Version: 2.1.0
Summary: Utilities for Google Media Downloads and Resumable Uploads
Home-page: https://github.com/googleapis/google-resumable-media-python
Author: Google Cloud Platform
@@ -16,6 +16,7 @@
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Internet
Requires-Python: >= 3.6
Provides-Extra: requests
@@ -55,6 +56,6 @@
Apache 2.0 - See `the LICENSE`_ for more information.
-.. _the LICENSE:
https://github.com/googleapis/google-resumable-media-python/blob/master/LICENSE
+.. _the LICENSE:
https://github.com/googleapis/google-resumable-media-python/blob/main/LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-resumable-media-2.0.0/README.rst
new/google-resumable-media-2.1.0/README.rst
--- old/google-resumable-media-2.0.0/README.rst 2021-08-19 22:11:18.000000000
+0200
+++ new/google-resumable-media-2.1.0/README.rst 2021-10-25 19:33:28.000000000
+0200
@@ -31,4 +31,4 @@
Apache 2.0 - See `the LICENSE`_ for more information.
-.. _the LICENSE:
https://github.com/googleapis/google-resumable-media-python/blob/master/LICENSE
+.. _the LICENSE:
https://github.com/googleapis/google-resumable-media-python/blob/main/LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/_async_resumable_media/_upload.py
new/google-resumable-media-2.1.0/google/_async_resumable_media/_upload.py
--- old/google-resumable-media-2.0.0/google/_async_resumable_media/_upload.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/google/_async_resumable_media/_upload.py
2021-10-25 19:33:28.000000000 +0200
@@ -277,7 +277,7 @@
checksum_object =
sync_helpers._get_checksum_object(self._checksum_type)
- if checksum_object:
+ if checksum_object is not None:
checksum_object.update(data)
actual_checksum = sync_helpers.prepare_checksum_digest(
checksum_object.digest()
@@ -635,7 +635,7 @@
"""
status_code = _helpers.require_status_code(
response,
- (http.client.OK, _async_resumable_media.PERMANENT_REDIRECT),
+ (http.client.OK, http.client.PERMANENT_REDIRECT),
self._get_status_code,
callback=self._make_invalid,
)
@@ -776,7 +776,7 @@
"""
_helpers.require_status_code(
response,
- (_async_resumable_media.PERMANENT_REDIRECT,),
+ (http.client.PERMANENT_REDIRECT,),
self._get_status_code,
)
headers = self._get_headers(response)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/_async_resumable_media/requests/__init__.py
new/google-resumable-media-2.1.0/google/_async_resumable_media/requests/__init__.py
---
old/google-resumable-media-2.0.0/google/_async_resumable_media/requests/__init__.py
2021-08-19 22:11:18.000000000 +0200
+++
new/google-resumable-media-2.1.0/google/_async_resumable_media/requests/__init__.py
2021-10-25 19:33:28.000000000 +0200
@@ -604,11 +604,11 @@
upload._chunk_size = 4
# Make three fake responses.
fake_response0 = requests.Response()
- fake_response0.status_code = resumable_media.PERMANENT_REDIRECT
+ fake_response0.status_code = http.client.PERMANENT_REDIRECT
fake_response0.headers['range'] = 'bytes=0-3'
fake_response1 = requests.Response()
- fake_response1.status_code = resumable_media.PERMANENT_REDIRECT
+ fake_response1.status_code = http.client.PERMANENT_REDIRECT
fake_response1.headers['range'] = 'bytes=0-7'
fake_response2 = requests.Response()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/_async_resumable_media/requests/download.py
new/google-resumable-media-2.1.0/google/_async_resumable_media/requests/download.py
---
old/google-resumable-media-2.0.0/google/_async_resumable_media/requests/download.py
2021-08-19 22:11:18.000000000 +0200
+++
new/google-resumable-media-2.1.0/google/_async_resumable_media/requests/download.py
2021-10-25 19:33:28.000000000 +0200
@@ -90,10 +90,7 @@
self._stream.write(chunk)
local_checksum_object.update(chunk)
- if expected_checksum is None:
- return
-
- else:
+ if expected_checksum is not None:
actual_checksum = sync_helpers.prepare_checksum_digest(
checksum_object.digest()
)
@@ -216,9 +213,7 @@
self._stream.write(chunk)
checksum_object.update(chunk)
- if expected_checksum is None:
- return
- else:
+ if expected_checksum is not None:
actual_checksum = sync_helpers.prepare_checksum_digest(
checksum_object.digest()
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/resumable_media/_helpers.py
new/google-resumable-media-2.1.0/google/resumable_media/_helpers.py
--- old/google-resumable-media-2.0.0/google/resumable_media/_helpers.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/google/resumable_media/_helpers.py
2021-10-25 19:33:28.000000000 +0200
@@ -20,7 +20,6 @@
import hashlib
import logging
import random
-import time
import warnings
from google.resumable_media import common
@@ -95,7 +94,8 @@
"""
status_code = get_status_code(response)
if status_code not in status_codes:
- callback()
+ if status_code not in common.RETRYABLE:
+ callback()
raise common.InvalidResponse(
response,
"Request failed with status code",
@@ -134,78 +134,6 @@
return new_base_wait, new_base_wait + 0.001 * jitter_ms
-def wait_and_retry(func, get_status_code, retry_strategy):
- """Attempts to retry a call to ``func`` until success.
-
- Expects ``func`` to return an HTTP response and uses ``get_status_code``
- to check if the response is retry-able.
-
- ``func`` is expected to raise a failure status code as a
- common.InvalidResponse, at which point this method will check the code
- against the common.RETRIABLE list of retriable status codes.
-
- Will retry until :meth:`~.RetryStrategy.retry_allowed` (on the current
- ``retry_strategy``) returns :data:`False`. Uses
- :func:`calculate_retry_wait` to double the wait time (with jitter) after
- each attempt.
-
- Args:
- func (Callable): A callable that takes no arguments and produces
- an HTTP response which will be checked as retry-able.
- get_status_code (Callable[Any, int]): Helper to get a status code
- from a response.
- retry_strategy (~google.resumable_media.common.RetryStrategy): The
- strategy to use if the request fails and must be retried.
-
- Returns:
- object: The return value of ``func``.
- """
- total_sleep = 0.0
- num_retries = 0
- # base_wait will be multiplied by the multiplier on the first retry.
- base_wait = float(retry_strategy.initial_delay) / retry_strategy.multiplier
-
- # Set the retriable_exception_type if possible. We expect requests to be
- # present here and the transport to be using requests.exceptions errors,
- # but due to loose coupling with the transport layer we can't guarantee it.
- try:
- connection_error_exceptions = _get_connection_error_classes()
- except ImportError:
- # We don't know the correct classes to use to catch connection errors,
- # so an empty tuple here communicates "catch no exceptions".
- connection_error_exceptions = ()
-
- while True: # return on success or when retries exhausted.
- error = None
- try:
- response = func()
- except connection_error_exceptions as e:
- error = e # Fall through to retry, if there are retries left.
- except common.InvalidResponse as e:
- # An InvalidResponse is only retriable if its status code matches.
- # The `process_response()` method on a Download or Upload method
- # will convert the status code into an exception.
- if get_status_code(e.response) in common.RETRYABLE:
- error = e # Fall through to retry, if there are retries left.
- else:
- raise # If the status code is not retriable, raise w/o retry.
- else:
- return response
-
- if not retry_strategy.retry_allowed(total_sleep, num_retries):
- # Retries are exhausted and no acceptable response was received.
- # Raise the retriable_error.
- raise error
-
- base_wait, wait_time = calculate_retry_wait(
- base_wait, retry_strategy.max_sleep, retry_strategy.multiplier
- )
-
- num_retries += 1
- total_sleep += wait_time
- time.sleep(wait_time)
-
-
def _get_crc32c_object():
"""Get crc32c object
Attempt to use the Google-CRC32c package. If it isn't available, try
@@ -374,22 +302,6 @@
raise ValueError("checksum must be ``'md5'``, ``'crc32c'`` or
``None``")
-def _get_connection_error_classes():
- """Get the exception error classes.
-
- Requests is a soft dependency here so that multiple transport layers can be
- added in the future. This code is in a separate function here so that the
- test framework can override its behavior to simulate requests being
- unavailable."""
-
- import requests.exceptions
-
- return (
- requests.exceptions.ConnectionError,
- requests.exceptions.ChunkedEncodingError,
- )
-
-
class _DoNothingHash(object):
"""Do-nothing hash object.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/resumable_media/_upload.py
new/google-resumable-media-2.1.0/google/resumable_media/_upload.py
--- old/google-resumable-media-2.0.0/google/resumable_media/_upload.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/google/resumable_media/_upload.py
2021-10-25 19:33:28.000000000 +0200
@@ -289,7 +289,7 @@
raise TypeError("`data` must be bytes, received", type(data))
checksum_object = _helpers._get_checksum_object(self._checksum_type)
- if checksum_object:
+ if checksum_object is not None:
checksum_object.update(data)
actual_checksum =
_helpers.prepare_checksum_digest(checksum_object.digest())
metadata_key = _helpers._get_metadata_key(self._checksum_type)
@@ -670,7 +670,7 @@
"""
status_code = _helpers.require_status_code(
response,
- (http.client.OK, resumable_media.PERMANENT_REDIRECT),
+ (http.client.OK, http.client.PERMANENT_REDIRECT),
self._get_status_code,
callback=self._make_invalid,
)
@@ -814,7 +814,7 @@
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
_helpers.require_status_code(
- response, (resumable_media.PERMANENT_REDIRECT,),
self._get_status_code
+ response, (http.client.PERMANENT_REDIRECT,), self._get_status_code
)
headers = self._get_headers(response)
if _helpers.RANGE_HEADER in headers:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/resumable_media/common.py
new/google-resumable-media-2.1.0/google/resumable_media/common.py
--- old/google-resumable-media-2.0.0/google/resumable_media/common.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/google/resumable_media/common.py
2021-10-25 19:33:28.000000000 +0200
@@ -25,35 +25,39 @@
UPLOAD_CHUNK_SIZE = 262144 # 256 * 1024
"""int: Chunks in a resumable upload must come in multiples of 256 KB."""
-PERMANENT_REDIRECT = 308
+
+PERMANENT_REDIRECT = http.client.PERMANENT_REDIRECT
"""int: Permanent redirect status code.
+.. note::
+ This is a backward-compatibility alias.
+
It is used by Google services to indicate some (but not all) of
a resumable upload has been completed.
-``http.client.PERMANENT_REDIRECT`` was added in Python 3.5, so
-can't be used in a "general" code base.
-
For more information, see `RFC 7238`_.
.. _RFC 7238: https://tools.ietf.org/html/rfc7238
"""
-TOO_MANY_REQUESTS = 429
+
+TOO_MANY_REQUESTS = http.client.TOO_MANY_REQUESTS
"""int: Status code indicating rate-limiting.
-``http.client.TOO_MANY_REQUESTS`` was added in Python 3.3, so
-can't be used in a "general" code base.
+.. note::
+ This is a backward-compatibility alias.
For more information, see `RFC 6585`_.
.. _RFC 6585: https://tools.ietf.org/html/rfc6585#section-4
"""
+
MAX_SLEEP = 64.0
"""float: Maximum amount of time allowed between requests.
Used during the retry process for sleep after a failed request.
Chosen since it is the power of two nearest to one minute.
"""
+
MAX_CUMULATIVE_RETRY = 600.0
"""float: Maximum total sleep time allowed during retry process.
@@ -62,7 +66,8 @@
"""
RETRYABLE = (
- TOO_MANY_REQUESTS, # 429
+ http.client.TOO_MANY_REQUESTS, # 429
+ http.client.REQUEST_TIMEOUT, # 408
http.client.INTERNAL_SERVER_ERROR, # 500
http.client.BAD_GATEWAY, # 502
http.client.SERVICE_UNAVAILABLE, # 503
@@ -158,9 +163,10 @@
"""Check if another retry is allowed.
Args:
- total_sleep (float): The amount of sleep accumulated by the caller.
- num_retries (int): The number of retries already attempted by
- the caller.
+ total_sleep (float): With another retry, the amount of sleep that
+ will be accumulated by the caller.
+ num_retries (int): With another retry, the number of retries that
+ will be attempted by the caller.
Returns:
bool: Indicating if another retry is allowed (depending on either
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/resumable_media/requests/__init__.py
new/google-resumable-media-2.1.0/google/resumable_media/requests/__init__.py
---
old/google-resumable-media-2.0.0/google/resumable_media/requests/__init__.py
2021-08-19 22:11:18.000000000 +0200
+++
new/google-resumable-media-2.1.0/google/resumable_media/requests/__init__.py
2021-10-25 19:33:28.000000000 +0200
@@ -604,11 +604,11 @@
upload._chunk_size = 4
# Make three fake responses.
fake_response0 = requests.Response()
- fake_response0.status_code = resumable_media.PERMANENT_REDIRECT
+ fake_response0.status_code = http.client.PERMANENT_REDIRECT
fake_response0.headers['range'] = 'bytes=0-3'
fake_response1 = requests.Response()
- fake_response1.status_code = resumable_media.PERMANENT_REDIRECT
+ fake_response1.status_code = http.client.PERMANENT_REDIRECT
fake_response1.headers['range'] = 'bytes=0-7'
fake_response2 = requests.Response()
@@ -631,7 +631,7 @@
>>> response0 = upload.transmit_next_chunk(transport)
>>> response0
- <Response [308]>
+ <Response [HTTPStatus.PERMANENT_REDIRECT]>
>>> upload.finished
False
>>> upload.bytes_uploaded == upload.chunk_size
@@ -639,7 +639,7 @@
>>>
>>> response1 = upload.transmit_next_chunk(transport)
>>> response1
- <Response [308]>
+ <Response [HTTPStatus.PERMANENT_REDIRECT]>
>>> upload.finished
False
>>> upload.bytes_uploaded == 2 * upload.chunk_size
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/resumable_media/requests/_request_helpers.py
new/google-resumable-media-2.1.0/google/resumable_media/requests/_request_helpers.py
---
old/google-resumable-media-2.0.0/google/resumable_media/requests/_request_helpers.py
2021-08-19 22:11:18.000000000 +0200
+++
new/google-resumable-media-2.1.0/google/resumable_media/requests/_request_helpers.py
2021-10-25 19:33:28.000000000 +0200
@@ -17,9 +17,13 @@
This utilities are explicitly catered to ``requests``-like transports.
"""
+import requests.exceptions
+import urllib3.exceptions
-from google.resumable_media import common
+import time
+from google.resumable_media import common
+from google.resumable_media import _helpers
_DEFAULT_RETRY_STRATEGY = common.RetryStrategy()
_SINGLE_GET_CHUNK_SIZE = 8192
@@ -30,6 +34,13 @@
# The number of seconds to wait between bytes sent from the server.
_DEFAULT_READ_TIMEOUT = 60
+_CONNECTION_ERROR_CLASSES = (
+ requests.exceptions.ConnectionError,
+ requests.exceptions.ChunkedEncodingError,
+ urllib3.exceptions.ProtocolError,
+ ConnectionError, # Python 3.x only, superclass of ConnectionResetError.
+)
+
class RequestsMixin(object):
"""Mix-in class implementing ``requests``-specific behavior.
@@ -93,3 +104,69 @@
)
response._content_consumed = True
return response._content
+
+
+def wait_and_retry(func, get_status_code, retry_strategy):
+ """Attempts to retry a call to ``func`` until success.
+
+ Expects ``func`` to return an HTTP response and uses ``get_status_code``
+ to check if the response is retry-able.
+
+ ``func`` is expected to raise a failure status code as a
+ common.InvalidResponse, at which point this method will check the code
+ against the common.RETRIABLE list of retriable status codes.
+
+ Will retry until :meth:`~.RetryStrategy.retry_allowed` (on the current
+ ``retry_strategy``) returns :data:`False`. Uses
+ :func:`_helpers.calculate_retry_wait` to double the wait time (with jitter)
+ after each attempt.
+
+ Args:
+ func (Callable): A callable that takes no arguments and produces
+ an HTTP response which will be checked as retry-able.
+ get_status_code (Callable[Any, int]): Helper to get a status code
+ from a response.
+ retry_strategy (~google.resumable_media.common.RetryStrategy): The
+ strategy to use if the request fails and must be retried.
+
+ Returns:
+ object: The return value of ``func``.
+ """
+ total_sleep = 0.0
+ num_retries = 0
+ # base_wait will be multiplied by the multiplier on the first retry.
+ base_wait = float(retry_strategy.initial_delay) / retry_strategy.multiplier
+
+ # Set the retriable_exception_type if possible. We expect requests to be
+ # present here and the transport to be using requests.exceptions errors,
+ # but due to loose coupling with the transport layer we can't guarantee it.
+
+ while True: # return on success or when retries exhausted.
+ error = None
+ try:
+ response = func()
+ except _CONNECTION_ERROR_CLASSES as e:
+ error = e # Fall through to retry, if there are retries left.
+ except common.InvalidResponse as e:
+ # An InvalidResponse is only retriable if its status code matches.
+ # The `process_response()` method on a Download or Upload method
+ # will convert the status code into an exception.
+ if get_status_code(e.response) in common.RETRYABLE:
+ error = e # Fall through to retry, if there are retries left.
+ else:
+ raise # If the status code is not retriable, raise w/o retry.
+ else:
+ return response
+
+ base_wait, wait_time = _helpers.calculate_retry_wait(
+ base_wait, retry_strategy.max_sleep, retry_strategy.multiplier
+ )
+ num_retries += 1
+ total_sleep += wait_time
+
+ # Check if (another) retry is allowed. If retries are exhausted and
+ # no acceptable response was received, raise the retriable error.
+ if not retry_strategy.retry_allowed(total_sleep, num_retries):
+ raise error
+
+ time.sleep(wait_time)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/resumable_media/requests/download.py
new/google-resumable-media-2.1.0/google/resumable_media/requests/download.py
---
old/google-resumable-media-2.0.0/google/resumable_media/requests/download.py
2021-08-19 22:11:18.000000000 +0200
+++
new/google-resumable-media-2.1.0/google/resumable_media/requests/download.py
2021-10-25 19:33:28.000000000 +0200
@@ -106,9 +106,7 @@
self._stream.write(chunk)
local_checksum_object.update(chunk)
- if expected_checksum is None:
- return
- else:
+ if expected_checksum is not None:
actual_checksum =
_helpers.prepare_checksum_digest(checksum_object.digest())
if actual_checksum != expected_checksum:
msg = _CHECKSUM_MISMATCH.format(
@@ -173,7 +171,7 @@
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
@@ -242,9 +240,7 @@
checksum_object.update(chunk)
response._content_consumed = True
- if expected_checksum is None:
- return
- else:
+ if expected_checksum is not None:
actual_checksum =
_helpers.prepare_checksum_digest(checksum_object.digest())
if actual_checksum != expected_checksum:
@@ -310,7 +306,7 @@
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
@@ -385,7 +381,7 @@
self._process_response(result)
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
@@ -461,7 +457,7 @@
self._process_response(result)
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google/resumable_media/requests/upload.py
new/google-resumable-media-2.1.0/google/resumable_media/requests/upload.py
--- old/google-resumable-media-2.0.0/google/resumable_media/requests/upload.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/google/resumable_media/requests/upload.py
2021-10-25 19:33:28.000000000 +0200
@@ -20,7 +20,6 @@
from google.resumable_media import _upload
-from google.resumable_media import _helpers
from google.resumable_media.requests import _request_helpers
@@ -80,7 +79,7 @@
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
@@ -151,7 +150,7 @@
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
@@ -418,7 +417,7 @@
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
@@ -476,7 +475,7 @@
...
>>> error
InvalidResponse('Request failed with status code', 400,
- 'Expected one of', <HTTPStatus.OK: 200>, 308)
+ 'Expected one of', <HTTPStatus.OK: 200>,
<HTTPStatus.PERMANENT_REDIRECT: 308>)
>>> error.response
<Response [400]>
@@ -496,7 +495,7 @@
Raises:
~google.resumable_media.common.InvalidResponse: If the status
- code is not 200 or 308.
+ code is not 200 or http.client.PERMANENT_REDIRECT.
~google.resumable_media.common.DataCorruption: If this is the final
chunk, a checksum validation was requested, and the checksum
does not match or is not available.
@@ -513,7 +512,7 @@
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
@@ -552,6 +551,6 @@
return result
- return _helpers.wait_and_retry(
+ return _request_helpers.wait_and_retry(
retriable_request, self._get_status_code, self._retry_strategy
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/google_resumable_media.egg-info/PKG-INFO
new/google-resumable-media-2.1.0/google_resumable_media.egg-info/PKG-INFO
--- old/google-resumable-media-2.0.0/google_resumable_media.egg-info/PKG-INFO
2021-08-19 22:13:53.000000000 +0200
+++ new/google-resumable-media-2.1.0/google_resumable_media.egg-info/PKG-INFO
2021-10-25 19:36:02.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: google-resumable-media
-Version: 2.0.0
+Version: 2.1.0
Summary: Utilities for Google Media Downloads and Resumable Uploads
Home-page: https://github.com/googleapis/google-resumable-media-python
Author: Google Cloud Platform
@@ -16,6 +16,7 @@
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
Classifier: Topic :: Internet
Requires-Python: >= 3.6
Provides-Extra: requests
@@ -55,6 +56,6 @@
Apache 2.0 - See `the LICENSE`_ for more information.
-.. _the LICENSE:
https://github.com/googleapis/google-resumable-media-python/blob/master/LICENSE
+.. _the LICENSE:
https://github.com/googleapis/google-resumable-media-python/blob/main/LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/google-resumable-media-2.0.0/setup.py
new/google-resumable-media-2.1.0/setup.py
--- old/google-resumable-media-2.0.0/setup.py 2021-08-19 22:11:18.000000000
+0200
+++ new/google-resumable-media-2.1.0/setup.py 2021-10-25 19:33:28.000000000
+0200
@@ -35,7 +35,7 @@
setuptools.setup(
name='google-resumable-media',
- version = "2.0.0",
+ version = "2.1.0",
description='Utilities for Google Media Downloads and Resumable Uploads',
author='Google Cloud Platform',
author_email='[email protected]',
@@ -61,6 +61,7 @@
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
+ 'Programming Language :: Python :: 3.10',
'Topic :: Internet',
],
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/tests/system/requests/test_upload.py
new/google-resumable-media-2.1.0/tests/system/requests/test_upload.py
--- old/google-resumable-media-2.0.0/tests/system/requests/test_upload.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/tests/system/requests/test_upload.py
2021-10-25 19:33:28.000000000 +0200
@@ -182,7 +182,7 @@
)
else:
assert upload.bytes_uploaded == num_chunks * upload.chunk_size
- assert response.status_code == resumable_media.PERMANENT_REDIRECT
+ assert response.status_code == http.client.PERMANENT_REDIRECT
return num_chunks
@@ -427,7 +427,7 @@
upload._bytes_uploaded = 0 # Make ``bytes_uploaded`` wrong as well.
# Recover the (artifically) invalid upload.
response = upload.recover(transport)
- assert response.status_code == resumable_media.PERMANENT_REDIRECT
+ assert response.status_code == http.client.PERMANENT_REDIRECT
assert not upload.invalid
assert upload.bytes_uploaded == chunk_size
assert stream.tell() == chunk_size
@@ -456,7 +456,7 @@
check_initiate(response, upload, stream, authorized_transport, metadata)
# Make the first request.
response = upload.transmit_next_chunk(authorized_transport)
- assert response.status_code == resumable_media.PERMANENT_REDIRECT
+ assert response.status_code == http.client.PERMANENT_REDIRECT
# Call upload.recover().
sabotage_and_recover(upload, stream, authorized_transport, chunk_size)
# Now stream what remains.
@@ -508,7 +508,7 @@
assert not upload.finished
assert upload.bytes_uploaded == end_byte + 1
- assert response.status_code == resumable_media.PERMANENT_REDIRECT
+ assert response.status_code == http.client.PERMANENT_REDIRECT
assert response.content == b""
self._check_range_sent(response, start_byte, end_byte, "*")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/tests/unit/requests/test__helpers.py
new/google-resumable-media-2.1.0/tests/unit/requests/test__helpers.py
--- old/google-resumable-media-2.0.0/tests/unit/requests/test__helpers.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/tests/unit/requests/test__helpers.py
2021-10-25 19:33:28.000000000 +0200
@@ -15,7 +15,12 @@
import http.client
import mock
+import pytest
+import requests.exceptions
+import urllib3.exceptions
+
+from google.resumable_media import common
from google.resumable_media.requests import _request_helpers
EXPECTED_TIMEOUT = (61, 60)
@@ -57,3 +62,309 @@
def _make_response(status_code):
return mock.Mock(status_code=status_code, spec=["status_code"])
+
+
+def _get_status_code(response):
+ return response.status_code
+
+
+class Test_wait_and_retry(object):
+ def test_success_no_retry(self):
+ truthy = http.client.OK
+ assert truthy not in common.RETRYABLE
+ response = _make_response(truthy)
+
+ func = mock.Mock(return_value=response, spec=[])
+ retry_strategy = common.RetryStrategy()
+ ret_val = _request_helpers.wait_and_retry(
+ func, _get_status_code, retry_strategy
+ )
+
+ assert ret_val is response
+ func.assert_called_once_with()
+
+ @mock.patch("time.sleep")
+ @mock.patch("random.randint")
+ def test_success_with_retry(self, randint_mock, sleep_mock):
+ randint_mock.side_effect = [125, 625, 375]
+
+ status_codes = (
+ http.client.INTERNAL_SERVER_ERROR,
+ http.client.BAD_GATEWAY,
+ http.client.SERVICE_UNAVAILABLE,
+ http.client.NOT_FOUND,
+ )
+ responses = [_make_response(status_code) for status_code in
status_codes]
+
+ def raise_response():
+ raise common.InvalidResponse(responses.pop(0))
+
+ func = mock.Mock(side_effect=raise_response)
+
+ retry_strategy = common.RetryStrategy()
+ try:
+ _request_helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
+ except common.InvalidResponse as e:
+ ret_val = e.response
+
+ assert ret_val.status_code == status_codes[-1]
+ assert status_codes[-1] not in common.RETRYABLE
+
+ assert func.call_count == 4
+ assert func.mock_calls == [mock.call()] * 4
+
+ assert randint_mock.call_count == 3
+ assert randint_mock.mock_calls == [mock.call(0, 1000)] * 3
+
+ assert sleep_mock.call_count == 3
+ sleep_mock.assert_any_call(1.125)
+ sleep_mock.assert_any_call(2.625)
+ sleep_mock.assert_any_call(4.375)
+
+ @mock.patch("time.sleep")
+ @mock.patch("random.randint")
+ def test_success_with_retry_custom_delay(self, randint_mock, sleep_mock):
+ randint_mock.side_effect = [125, 625, 375]
+
+ status_codes = (
+ http.client.INTERNAL_SERVER_ERROR,
+ http.client.BAD_GATEWAY,
+ http.client.SERVICE_UNAVAILABLE,
+ http.client.NOT_FOUND,
+ )
+ responses = [_make_response(status_code) for status_code in
status_codes]
+
+ def raise_response():
+ raise common.InvalidResponse(responses.pop(0))
+
+ func = mock.Mock(side_effect=raise_response)
+
+ retry_strategy = common.RetryStrategy(initial_delay=3.0, multiplier=4)
+ try:
+ _request_helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
+ except common.InvalidResponse as e:
+ ret_val = e.response
+
+ assert ret_val.status_code == status_codes[-1]
+ assert status_codes[-1] not in common.RETRYABLE
+
+ assert func.call_count == 4
+ assert func.mock_calls == [mock.call()] * 4
+
+ assert randint_mock.call_count == 3
+ assert randint_mock.mock_calls == [mock.call(0, 1000)] * 3
+
+ assert sleep_mock.call_count == 3
+ sleep_mock.assert_any_call(3.125) # initial delay 3 + jitter 0.125
+ sleep_mock.assert_any_call(
+ 12.625
+ ) # previous delay 3 * multiplier 4 + jitter 0.625
+ sleep_mock.assert_any_call(
+ 48.375
+ ) # previous delay 12 * multiplier 4 + jitter 0.375
+
+ @mock.patch("time.sleep")
+ @mock.patch("random.randint")
+ def test_success_with_retry_connection_error(self, randint_mock,
sleep_mock):
+ randint_mock.side_effect = [125, 625, 375]
+
+ response = _make_response(http.client.NOT_FOUND)
+ responses = [
+ ConnectionResetError, # Subclass of ConnectionError
+ urllib3.exceptions.ConnectionError,
+ requests.exceptions.ConnectionError,
+ response,
+ ]
+ func = mock.Mock(side_effect=responses, spec=[])
+
+ retry_strategy = common.RetryStrategy()
+ ret_val = _request_helpers.wait_and_retry(
+ func, _get_status_code, retry_strategy
+ )
+
+ assert ret_val == responses[-1]
+
+ assert func.call_count == 4
+ assert func.mock_calls == [mock.call()] * 4
+
+ assert randint_mock.call_count == 3
+ assert randint_mock.mock_calls == [mock.call(0, 1000)] * 3
+
+ assert sleep_mock.call_count == 3
+ sleep_mock.assert_any_call(1.125)
+ sleep_mock.assert_any_call(2.625)
+ sleep_mock.assert_any_call(4.375)
+
+ @mock.patch(u"time.sleep")
+ @mock.patch(u"random.randint")
+ def test_success_with_retry_chunked_encoding_error(self, randint_mock,
sleep_mock):
+ randint_mock.side_effect = [125, 625, 375]
+
+ response = _make_response(http.client.NOT_FOUND)
+ responses = [
+ requests.exceptions.ChunkedEncodingError,
+ requests.exceptions.ChunkedEncodingError,
+ response,
+ ]
+ func = mock.Mock(side_effect=responses, spec=[])
+
+ retry_strategy = common.RetryStrategy()
+ ret_val = _request_helpers.wait_and_retry(
+ func, _get_status_code, retry_strategy
+ )
+
+ assert ret_val == responses[-1]
+
+ assert func.call_count == 3
+ assert func.mock_calls == [mock.call()] * 3
+
+ assert randint_mock.call_count == 2
+ assert randint_mock.mock_calls == [mock.call(0, 1000)] * 2
+
+ assert sleep_mock.call_count == 2
+ sleep_mock.assert_any_call(1.125)
+ sleep_mock.assert_any_call(2.625)
+
+ @mock.patch("time.sleep")
+ @mock.patch("random.randint")
+ def test_retry_exceeds_max_cumulative(self, randint_mock, sleep_mock):
+ randint_mock.side_effect = [875, 0, 375, 500, 500, 250, 125]
+
+ status_codes = (
+ http.client.SERVICE_UNAVAILABLE,
+ http.client.GATEWAY_TIMEOUT,
+ common.TOO_MANY_REQUESTS,
+ http.client.INTERNAL_SERVER_ERROR,
+ http.client.SERVICE_UNAVAILABLE,
+ http.client.BAD_GATEWAY,
+ common.TOO_MANY_REQUESTS,
+ )
+ responses = [_make_response(status_code) for status_code in
status_codes]
+
+ def raise_response():
+ raise common.InvalidResponse(responses.pop(0))
+
+ func = mock.Mock(side_effect=raise_response)
+
+ retry_strategy = common.RetryStrategy(max_cumulative_retry=100.0)
+ try:
+ _request_helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
+ except common.InvalidResponse as e:
+ ret_val = e.response
+
+ assert ret_val.status_code == status_codes[-1]
+ assert status_codes[-1] in common.RETRYABLE
+
+ assert func.call_count == 7
+ assert func.mock_calls == [mock.call()] * 7
+
+ assert randint_mock.call_count == 7
+ assert randint_mock.mock_calls == [mock.call(0, 1000)] * 7
+
+ assert sleep_mock.call_count == 6
+ sleep_mock.assert_any_call(1.875)
+ sleep_mock.assert_any_call(2.0)
+ sleep_mock.assert_any_call(4.375)
+ sleep_mock.assert_any_call(8.5)
+ sleep_mock.assert_any_call(16.5)
+ sleep_mock.assert_any_call(32.25)
+
+ @mock.patch("time.sleep")
+ @mock.patch("random.randint")
+ def test_retry_exceeds_max_retries(self, randint_mock, sleep_mock):
+ randint_mock.side_effect = [875, 0, 375, 500, 500, 250, 125]
+
+ status_codes = (
+ http.client.SERVICE_UNAVAILABLE,
+ http.client.GATEWAY_TIMEOUT,
+ common.TOO_MANY_REQUESTS,
+ http.client.INTERNAL_SERVER_ERROR,
+ http.client.SERVICE_UNAVAILABLE,
+ http.client.BAD_GATEWAY,
+ common.TOO_MANY_REQUESTS,
+ )
+ responses = [_make_response(status_code) for status_code in
status_codes]
+
+ def raise_response():
+ raise common.InvalidResponse(responses.pop(0))
+
+ func = mock.Mock(side_effect=raise_response)
+
+ retry_strategy = common.RetryStrategy(max_retries=6)
+ try:
+ _request_helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
+ except common.InvalidResponse as e:
+ ret_val = e.response
+
+ assert ret_val.status_code == status_codes[-1]
+ assert status_codes[-1] in common.RETRYABLE
+
+ assert func.call_count == 7
+ assert func.mock_calls == [mock.call()] * 7
+
+ assert randint_mock.call_count == 7
+ assert randint_mock.mock_calls == [mock.call(0, 1000)] * 7
+
+ assert sleep_mock.call_count == 6
+ sleep_mock.assert_any_call(1.875)
+ sleep_mock.assert_any_call(2.0)
+ sleep_mock.assert_any_call(4.375)
+ sleep_mock.assert_any_call(8.5)
+ sleep_mock.assert_any_call(16.5)
+ sleep_mock.assert_any_call(32.25)
+
+ @mock.patch("time.sleep")
+ @mock.patch("random.randint")
+ def test_retry_zero_max_retries(self, randint_mock, sleep_mock):
+ randint_mock.side_effect = [875, 0, 375]
+
+ status_codes = (
+ http.client.SERVICE_UNAVAILABLE,
+ http.client.GATEWAY_TIMEOUT,
+ common.TOO_MANY_REQUESTS,
+ )
+ responses = [_make_response(status_code) for status_code in
status_codes]
+
+ def raise_response():
+ raise common.InvalidResponse(responses.pop(0))
+
+ func = mock.Mock(side_effect=raise_response)
+
+ retry_strategy = common.RetryStrategy(max_retries=0)
+ try:
+ _request_helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
+ except common.InvalidResponse as e:
+ ret_val = e.response
+
+ assert func.call_count == 1
+ assert func.mock_calls == [mock.call()] * 1
+ assert ret_val.status_code == status_codes[0]
+
+ assert randint_mock.call_count == 1
+ assert sleep_mock.call_count == 0
+
+ @mock.patch("time.sleep")
+ @mock.patch("random.randint")
+ def test_retry_exceeded_reraises_connection_error(self, randint_mock,
sleep_mock):
+ randint_mock.side_effect = [875, 0, 375, 500, 500, 250, 125]
+
+ responses = [requests.exceptions.ConnectionError] * 7
+ func = mock.Mock(side_effect=responses, spec=[])
+
+ retry_strategy = common.RetryStrategy(max_cumulative_retry=100.0)
+ with pytest.raises(requests.exceptions.ConnectionError):
+ _request_helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
+
+ assert func.call_count == 7
+ assert func.mock_calls == [mock.call()] * 7
+
+ assert randint_mock.call_count == 7
+ assert randint_mock.mock_calls == [mock.call(0, 1000)] * 7
+
+ assert sleep_mock.call_count == 6
+ sleep_mock.assert_any_call(1.875)
+ sleep_mock.assert_any_call(2.0)
+ sleep_mock.assert_any_call(4.375)
+ sleep_mock.assert_any_call(8.5)
+ sleep_mock.assert_any_call(16.5)
+ sleep_mock.assert_any_call(32.25)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/tests/unit/requests/test_upload.py
new/google-resumable-media-2.1.0/tests/unit/requests/test_upload.py
--- old/google-resumable-media-2.0.0/tests/unit/requests/test_upload.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/tests/unit/requests/test_upload.py
2021-10-25 19:33:28.000000000 +0200
@@ -18,7 +18,6 @@
import mock
-from google import resumable_media
import google.resumable_media.requests.upload as upload_mod
@@ -254,9 +253,7 @@
upload._chunk_size = chunk_size
# Make a fake 308 response.
response_headers = {"range": "bytes=0-{:d}".format(chunk_size - 1)}
- transport = self._chunk_mock(
- resumable_media.PERMANENT_REDIRECT, response_headers
- )
+ transport = self._chunk_mock(http.client.PERMANENT_REDIRECT,
response_headers)
# Check the state before the request.
assert upload._bytes_uploaded == 0
@@ -290,9 +287,7 @@
# Make a fake 308 response.
response_headers = {"range": "bytes=0-{:d}".format(chunk_size - 1)}
- transport = self._chunk_mock(
- resumable_media.PERMANENT_REDIRECT, response_headers
- )
+ transport = self._chunk_mock(http.client.PERMANENT_REDIRECT,
response_headers)
# Make request and check the return value (against the mock).
upload.transmit_next_chunk(transport, timeout=12.6)
@@ -320,7 +315,7 @@
end = 55555
headers = {"range": "bytes=0-{:d}".format(end)}
- transport = self._chunk_mock(resumable_media.PERMANENT_REDIRECT,
headers)
+ transport = self._chunk_mock(http.client.PERMANENT_REDIRECT, headers)
ret_val = upload.recover(transport)
assert ret_val is transport.request.return_value
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/tests/unit/test__helpers.py
new/google-resumable-media-2.1.0/tests/unit/test__helpers.py
--- old/google-resumable-media-2.0.0/tests/unit/test__helpers.py
2021-08-19 22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/tests/unit/test__helpers.py
2021-10-25 19:33:28.000000000 +0200
@@ -19,7 +19,6 @@
import mock
import pytest
-import requests.exceptions
from google.resumable_media import _helpers
from google.resumable_media import common
@@ -125,6 +124,28 @@
assert error.args[3:] == status_codes
callback.assert_called_once_with()
+ def test_retryable_failure_without_callback(self):
+ status_codes = (http.client.OK,)
+ retryable_responses = [
+ _make_response(status_code) for status_code in common.RETRYABLE
+ ]
+ callback = mock.Mock(spec=[])
+ for retryable_response in retryable_responses:
+ with pytest.raises(common.InvalidResponse) as exc_info:
+ _helpers.require_status_code(
+ retryable_response,
+ status_codes,
+ self._get_status_code,
+ callback=callback,
+ )
+
+ error = exc_info.value
+ assert error.response is retryable_response
+ assert len(error.args) == 4
+ assert error.args[1] == retryable_response.status_code
+ assert error.args[3:] == status_codes
+ callback.assert_not_called()
+
class Test_calculate_retry_wait(object):
@mock.patch("random.randint", return_value=125)
@@ -160,262 +181,10 @@
randint_mock.assert_called_once_with(0, 1000)
-class Test_wait_and_retry(object):
- def test_success_no_retry(self):
- truthy = http.client.OK
- assert truthy not in common.RETRYABLE
- response = _make_response(truthy)
-
- func = mock.Mock(return_value=response, spec=[])
- retry_strategy = common.RetryStrategy()
- ret_val = _helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
-
- assert ret_val is response
- func.assert_called_once_with()
-
- @mock.patch("time.sleep")
- @mock.patch("random.randint")
- def test_success_with_retry(self, randint_mock, sleep_mock):
- randint_mock.side_effect = [125, 625, 375]
-
- status_codes = (
- http.client.INTERNAL_SERVER_ERROR,
- http.client.BAD_GATEWAY,
- http.client.SERVICE_UNAVAILABLE,
- http.client.NOT_FOUND,
- )
- responses = [_make_response(status_code) for status_code in
status_codes]
-
- def raise_response():
- raise common.InvalidResponse(responses.pop(0))
-
- func = mock.Mock(side_effect=raise_response)
-
- retry_strategy = common.RetryStrategy()
- try:
- _helpers.wait_and_retry(func, _get_status_code, retry_strategy)
- except common.InvalidResponse as e:
- ret_val = e.response
-
- assert ret_val.status_code == status_codes[-1]
- assert status_codes[-1] not in common.RETRYABLE
-
- assert func.call_count == 4
- assert func.mock_calls == [mock.call()] * 4
-
- assert randint_mock.call_count == 3
- assert randint_mock.mock_calls == [mock.call(0, 1000)] * 3
-
- assert sleep_mock.call_count == 3
- sleep_mock.assert_any_call(1.125)
- sleep_mock.assert_any_call(2.625)
- sleep_mock.assert_any_call(4.375)
-
- @mock.patch("time.sleep")
- @mock.patch("random.randint")
- def test_success_with_retry_custom_delay(self, randint_mock, sleep_mock):
- randint_mock.side_effect = [125, 625, 375]
-
- status_codes = (
- http.client.INTERNAL_SERVER_ERROR,
- http.client.BAD_GATEWAY,
- http.client.SERVICE_UNAVAILABLE,
- http.client.NOT_FOUND,
- )
- responses = [_make_response(status_code) for status_code in
status_codes]
-
- def raise_response():
- raise common.InvalidResponse(responses.pop(0))
-
- func = mock.Mock(side_effect=raise_response)
-
- retry_strategy = common.RetryStrategy(initial_delay=3.0, multiplier=4)
- try:
- _helpers.wait_and_retry(func, _get_status_code, retry_strategy)
- except common.InvalidResponse as e:
- ret_val = e.response
-
- assert ret_val.status_code == status_codes[-1]
- assert status_codes[-1] not in common.RETRYABLE
-
- assert func.call_count == 4
- assert func.mock_calls == [mock.call()] * 4
-
- assert randint_mock.call_count == 3
- assert randint_mock.mock_calls == [mock.call(0, 1000)] * 3
-
- assert sleep_mock.call_count == 3
- sleep_mock.assert_any_call(3.125) # initial delay 3 + jitter 0.125
- sleep_mock.assert_any_call(
- 12.625
- ) # previous delay 3 * multiplier 4 + jitter 0.625
- sleep_mock.assert_any_call(
- 48.375
- ) # previous delay 12 * multiplier 4 + jitter 0.375
-
- @mock.patch("time.sleep")
- @mock.patch("random.randint")
- def test_success_with_retry_connection_error(self, randint_mock,
sleep_mock):
- randint_mock.side_effect = [125, 625, 375]
-
- response = _make_response(http.client.NOT_FOUND)
- responses = [
- requests.exceptions.ConnectionError,
- requests.exceptions.ConnectionError,
- requests.exceptions.ConnectionError,
- response,
- ]
- func = mock.Mock(side_effect=responses, spec=[])
-
- retry_strategy = common.RetryStrategy()
- ret_val = _helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
-
- assert ret_val == responses[-1]
-
- assert func.call_count == 4
- assert func.mock_calls == [mock.call()] * 4
-
- assert randint_mock.call_count == 3
- assert randint_mock.mock_calls == [mock.call(0, 1000)] * 3
-
- assert sleep_mock.call_count == 3
- sleep_mock.assert_any_call(1.125)
- sleep_mock.assert_any_call(2.625)
- sleep_mock.assert_any_call(4.375)
-
- @mock.patch(u"time.sleep")
- @mock.patch(u"random.randint")
- def test_success_with_retry_chunked_encoding_error(self, randint_mock,
sleep_mock):
- randint_mock.side_effect = [125, 625, 375]
-
- response = _make_response(http.client.NOT_FOUND)
- responses = [
- requests.exceptions.ChunkedEncodingError,
- requests.exceptions.ChunkedEncodingError,
- response,
- ]
- func = mock.Mock(side_effect=responses, spec=[])
-
- retry_strategy = common.RetryStrategy()
- ret_val = _helpers.wait_and_retry(func, _get_status_code,
retry_strategy)
-
- assert ret_val == responses[-1]
-
- assert func.call_count == 3
- assert func.mock_calls == [mock.call()] * 3
-
- assert randint_mock.call_count == 2
- assert randint_mock.mock_calls == [mock.call(0, 1000)] * 2
-
- assert sleep_mock.call_count == 2
- sleep_mock.assert_any_call(1.125)
- sleep_mock.assert_any_call(2.625)
-
- @mock.patch(u"time.sleep")
- @mock.patch(u"random.randint")
- def test_connection_import_error_failure(self, randint_mock, sleep_mock):
- randint_mock.side_effect = [125, 625, 375]
-
- response = _make_response(http.client.NOT_FOUND)
- responses = [
- requests.exceptions.ConnectionError,
- requests.exceptions.ConnectionError,
- requests.exceptions.ConnectionError,
- response,
- ]
-
- with mock.patch(
- "google.resumable_media._helpers._get_connection_error_classes",
- side_effect=ImportError,
- ):
- with pytest.raises(requests.exceptions.ConnectionError):
- func = mock.Mock(side_effect=responses, spec=[])
-
- retry_strategy = common.RetryStrategy()
- _helpers.wait_and_retry(func, _get_status_code, retry_strategy)
-
- @mock.patch("time.sleep")
- @mock.patch("random.randint")
- def test_retry_exceeds_max_cumulative(self, randint_mock, sleep_mock):
- randint_mock.side_effect = [875, 0, 375, 500, 500, 250, 125, 0]
-
- status_codes = (
- http.client.SERVICE_UNAVAILABLE,
- http.client.GATEWAY_TIMEOUT,
- common.TOO_MANY_REQUESTS,
- http.client.INTERNAL_SERVER_ERROR,
- http.client.SERVICE_UNAVAILABLE,
- http.client.BAD_GATEWAY,
- http.client.GATEWAY_TIMEOUT,
- common.TOO_MANY_REQUESTS,
- )
- responses = [_make_response(status_code) for status_code in
status_codes]
-
- def raise_response():
- raise common.InvalidResponse(responses.pop(0))
-
- func = mock.Mock(side_effect=raise_response)
-
- retry_strategy = common.RetryStrategy(max_cumulative_retry=100.0)
- try:
- _helpers.wait_and_retry(func, _get_status_code, retry_strategy)
- except common.InvalidResponse as e:
- ret_val = e.response
-
- assert ret_val.status_code == status_codes[-1]
- assert status_codes[-1] in common.RETRYABLE
-
- assert func.call_count == 8
- assert func.mock_calls == [mock.call()] * 8
-
- assert randint_mock.call_count == 7
- assert randint_mock.mock_calls == [mock.call(0, 1000)] * 7
-
- assert sleep_mock.call_count == 7
- sleep_mock.assert_any_call(1.875)
- sleep_mock.assert_any_call(2.0)
- sleep_mock.assert_any_call(4.375)
- sleep_mock.assert_any_call(8.5)
- sleep_mock.assert_any_call(16.5)
- sleep_mock.assert_any_call(32.25)
- sleep_mock.assert_any_call(64.125)
-
- @mock.patch("time.sleep")
- @mock.patch("random.randint")
- def test_retry_exceeded_reraises_connection_error(self, randint_mock,
sleep_mock):
- randint_mock.side_effect = [875, 0, 375, 500, 500, 250, 125]
-
- responses = [requests.exceptions.ConnectionError] * 8
- func = mock.Mock(side_effect=responses, spec=[])
-
- retry_strategy = common.RetryStrategy(max_cumulative_retry=100.0)
- with pytest.raises(requests.exceptions.ConnectionError):
- _helpers.wait_and_retry(func, _get_status_code, retry_strategy)
-
- assert func.call_count == 8
- assert func.mock_calls == [mock.call()] * 8
-
- assert randint_mock.call_count == 7
- assert randint_mock.mock_calls == [mock.call(0, 1000)] * 7
-
- assert sleep_mock.call_count == 7
- sleep_mock.assert_any_call(1.875)
- sleep_mock.assert_any_call(2.0)
- sleep_mock.assert_any_call(4.375)
- sleep_mock.assert_any_call(8.5)
- sleep_mock.assert_any_call(16.5)
- sleep_mock.assert_any_call(32.25)
- sleep_mock.assert_any_call(64.125)
-
-
def _make_response(status_code):
return mock.Mock(status_code=status_code, spec=["status_code"])
-def _get_status_code(response):
- return response.status_code
-
-
def _get_headers(response):
return response.headers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/google-resumable-media-2.0.0/tests/unit/test__upload.py
new/google-resumable-media-2.1.0/tests/unit/test__upload.py
--- old/google-resumable-media-2.0.0/tests/unit/test__upload.py 2021-08-19
22:11:18.000000000 +0200
+++ new/google-resumable-media-2.1.0/tests/unit/test__upload.py 2021-10-25
19:33:28.000000000 +0200
@@ -19,7 +19,6 @@
import mock
import pytest
-from google import resumable_media
from google.resumable_media import _helpers
from google.resumable_media import _upload
from google.resumable_media import common
@@ -721,7 +720,7 @@
assert len(error.args) == 5
assert error.args[1] == response.status_code
assert error.args[3] == http.client.OK
- assert error.args[4] == resumable_media.PERMANENT_REDIRECT
+ assert error.args[4] == http.client.PERMANENT_REDIRECT
# Make sure the upload is invalid after the failure.
assert upload.invalid
@@ -754,7 +753,7 @@
upload = _upload.ResumableUpload(RESUMABLE_URL, ONE_MB)
_fix_up_virtual(upload)
- response =
_make_response(status_code=resumable_media.PERMANENT_REDIRECT)
+ response = _make_response(status_code=http.client.PERMANENT_REDIRECT)
# Make sure the upload is valid before the failure.
assert not upload.invalid
with pytest.raises(common.InvalidResponse) as exc_info:
@@ -776,7 +775,7 @@
assert not upload.invalid
headers = {"range": "nights 1-81"}
response = _make_response(
- status_code=resumable_media.PERMANENT_REDIRECT, headers=headers
+ status_code=http.client.PERMANENT_REDIRECT, headers=headers
)
with pytest.raises(common.InvalidResponse) as exc_info:
upload._process_response(response, 81)
@@ -797,7 +796,7 @@
assert upload._bytes_uploaded == 0
headers = {"range": "bytes=0-171"}
response = _make_response(
- status_code=resumable_media.PERMANENT_REDIRECT, headers=headers
+ status_code=http.client.PERMANENT_REDIRECT, headers=headers
)
ret_val = upload._process_response(response, 172)
assert ret_val is None
@@ -975,7 +974,7 @@
assert error.response is response
assert len(error.args) == 4
assert error.args[1] == response.status_code
- assert error.args[3] == resumable_media.PERMANENT_REDIRECT
+ assert error.args[3] == http.client.PERMANENT_REDIRECT
# Make sure still invalid.
assert upload.invalid
@@ -988,7 +987,7 @@
upload._bytes_uploaded = mock.sentinel.not_zero
assert upload.bytes_uploaded != 0
- response =
_make_response(status_code=resumable_media.PERMANENT_REDIRECT)
+ response = _make_response(status_code=http.client.PERMANENT_REDIRECT)
ret_val = upload._process_recover_response(response)
assert ret_val is None
# Check the state of ``upload`` after.
@@ -1006,7 +1005,7 @@
headers = {"range": "bites=9-11"}
response = _make_response(
- status_code=resumable_media.PERMANENT_REDIRECT, headers=headers
+ status_code=http.client.PERMANENT_REDIRECT, headers=headers
)
with pytest.raises(common.InvalidResponse) as exc_info:
upload._process_recover_response(response)
@@ -1032,7 +1031,7 @@
end = 11
headers = {"range": "bytes=0-{:d}".format(end)}
response = _make_response(
- status_code=resumable_media.PERMANENT_REDIRECT, headers=headers
+ status_code=http.client.PERMANENT_REDIRECT, headers=headers
)
ret_val = upload._process_recover_response(response)
assert ret_val is None