Skip to content

Commit bd917b4

Browse files
andrewsgcojenco
andcommitted
feat: Integrate google-resumable-media (#1283)
Integrate the google-resumable-media library into python-storage. --------- Co-authored-by: cojenco <[email protected]>
1 parent 41e4016 commit bd917b4

32 files changed

+536
-377
lines changed

README.rst

+35
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,41 @@ Google APIs Client Libraries, in `Client Libraries Explained`_.
3737
.. _Storage Control API: https://cloud.google.com/storage/docs/reference/rpc/google.storage.control.v2
3838
.. _Client Libraries Explained: https://cloud.google.com/apis/docs/client-libraries-explained
3939

40+
Major Version Release Notes
41+
---------------------------
42+
43+
Preview Release
44+
~~~~~~~~~~~~~~~
45+
46+
Python Storage 3.0 is currently in a preview state. If you experience that
47+
backwards compatibility for your application is broken with this release for any
48+
reason, please let us know through the Github issues system. Thank you.
49+
50+
Exception Handling
51+
~~~~~~~~~~~~~~~~~~
52+
53+
In Python Storage 3.0, the dependency `google-resumable-media` was integrated.
54+
The `google-resumable-media` dependency included exceptions
55+
`google.resumable_media.common.InvalidResponse` and
56+
`google.resumable_media.common.DataCorruption`, which were often imported
57+
directly in user application code. The replacements for these exceptions are
58+
`google.cloud.storage.exceptions.InvalidResponse` and
59+
`google.cloud.storage.exceptions.DataCorruption`. Please update application code
60+
to import and use these exceptions instead.
61+
62+
For backwards compatibility, if `google-resumable-media` is installed, the new
63+
exceptions will be defined as subclasses of the old exceptions, so applications
64+
should continue to work without modification. This backwards compatibility
65+
feature may be removed in a future major version update.
66+
67+
Some users may be using the original exception classes from the
68+
`google-resumable-media` library without explicitly importing that library. So
69+
as not to break user applications following this pattern,
70+
`google-resumable-media` is still in the list of dependencies in this package's
71+
setup.py file. Applications which do not import directly from
72+
`google-resumable-media` can safely disregard this dependency. This backwards
73+
compatibility feature will be removed in a future major version update.
74+
4075
Quick Start
4176
-----------
4277

docs/storage/exceptions.rst

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Exceptions
2+
~~~~~~~~~
3+
4+
.. automodule:: google.cloud.storage.exceptions
5+
:members:
6+
:member-order: bysource
7+

google/cloud/storage/_helpers.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
from urllib.parse import urlunsplit
2626
from uuid import uuid4
2727

28-
from google import resumable_media
2928
from google.auth import environment_vars
29+
from google.cloud.storage import _media
3030
from google.cloud.storage.constants import _DEFAULT_TIMEOUT
3131
from google.cloud.storage.retry import DEFAULT_RETRY
3232
from google.cloud.storage.retry import DEFAULT_RETRY_IF_METAGENERATION_SPECIFIED
@@ -635,7 +635,7 @@ def _bucket_bound_hostname_url(host, scheme=None):
635635

636636

637637
def _api_core_retry_to_resumable_media_retry(retry, num_retries=None):
638-
"""Convert google.api.core.Retry to google.resumable_media.RetryStrategy.
638+
"""Convert google.api.core.Retry to google.cloud.storage._media.RetryStrategy.
639639
640640
Custom predicates are not translated.
641641
@@ -647,7 +647,7 @@ def _api_core_retry_to_resumable_media_retry(retry, num_retries=None):
647647
supported for backwards compatibility and is mutually exclusive with
648648
`retry`.
649649
650-
:rtype: google.resumable_media.RetryStrategy
650+
:rtype: google.cloud.storage._media.RetryStrategy
651651
:returns: A RetryStrategy with all applicable attributes copied from input,
652652
or a RetryStrategy with max_retries set to 0 if None was input.
653653
"""
@@ -656,16 +656,16 @@ def _api_core_retry_to_resumable_media_retry(retry, num_retries=None):
656656
raise ValueError("num_retries and retry arguments are mutually exclusive")
657657

658658
elif retry is not None:
659-
return resumable_media.RetryStrategy(
659+
return _media.RetryStrategy(
660660
max_sleep=retry._maximum,
661661
max_cumulative_retry=retry._deadline,
662662
initial_delay=retry._initial,
663663
multiplier=retry._multiplier,
664664
)
665665
elif num_retries is not None:
666-
return resumable_media.RetryStrategy(max_retries=num_retries)
666+
return _media.RetryStrategy(max_retries=num_retries)
667667
else:
668-
return resumable_media.RetryStrategy(max_retries=0)
668+
return _media.RetryStrategy(max_retries=0)
669669

670670

671671
def _get_invocation_id():

google/cloud/storage/_media/__init__.py

+3-28
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,23 @@
1414

1515
"""Utilities for Google Media Downloads and Resumable Uploads.
1616
17-
This package has some general purposes modules, e.g.
18-
:mod:`~google.resumable_media.common`, but the majority of the
19-
public interface will be contained in subpackages.
20-
2117
===========
2218
Subpackages
2319
===========
2420
2521
Each subpackage is tailored to a specific transport library:
2622
27-
* the :mod:`~google.resumable_media.requests` subpackage uses the ``requests``
23+
* the :mod:`~google.cloud.storage._media.requests` subpackage uses the ``requests``
2824
transport library.
2925
3026
.. _requests: http://docs.python-requests.org/
31-
32-
==========
33-
Installing
34-
==========
35-
36-
To install with `pip`_:
37-
38-
.. code-block:: console
39-
40-
$ pip install --upgrade google-resumable-media
41-
42-
.. _pip: https://pip.pypa.io/
4327
"""
4428

45-
46-
from google.resumable_media.common import DataCorruption
47-
from google.resumable_media.common import InvalidResponse
48-
from google.resumable_media.common import PERMANENT_REDIRECT
49-
from google.resumable_media.common import RetryStrategy
50-
from google.resumable_media.common import TOO_MANY_REQUESTS
51-
from google.resumable_media.common import UPLOAD_CHUNK_SIZE
29+
from google.cloud.storage._media.common import RetryStrategy
30+
from google.cloud.storage._media.common import UPLOAD_CHUNK_SIZE
5231

5332

5433
__all__ = [
55-
"DataCorruption",
56-
"InvalidResponse",
57-
"PERMANENT_REDIRECT",
5834
"RetryStrategy",
59-
"TOO_MANY_REQUESTS",
6035
"UPLOAD_CHUNK_SIZE",
6136
]

google/cloud/storage/_media/_download.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818
import http.client
1919
import re
2020

21-
from google.resumable_media import _helpers
22-
from google.resumable_media import common
21+
from google.cloud.storage._media import _helpers
22+
from google.cloud.storage._media import common
23+
from google.cloud.storage.exceptions import InvalidResponse
2324

2425

2526
_CONTENT_RANGE_RE = re.compile(
@@ -361,7 +362,7 @@ def _process_response(self, response):
361362
response (object): The HTTP response object (need headers).
362363
363364
Raises:
364-
~google.resumable_media.common.InvalidResponse: If the number
365+
~google.cloud.storage.exceptions.InvalidResponse: If the number
365366
of bytes in the body doesn't match the content length header.
366367
367368
.. _sans-I/O: https://sans-io.readthedocs.io/
@@ -398,7 +399,7 @@ def _process_response(self, response):
398399
num_bytes = int(content_length)
399400
if len(response_body) != num_bytes:
400401
self._make_invalid()
401-
raise common.InvalidResponse(
402+
raise InvalidResponse(
402403
response,
403404
"Response is different size than content-length",
404405
"Expected",
@@ -508,7 +509,7 @@ def get_range_info(response, get_headers, callback=_helpers.do_nothing):
508509
Tuple[int, int, int]: The start byte, end byte and total bytes.
509510
510511
Raises:
511-
~google.resumable_media.common.InvalidResponse: If the
512+
~google.cloud.storage.exceptions.InvalidResponse: If the
512513
``Content-Range`` header is not of the form
513514
``bytes {start}-{end}/{total}``.
514515
"""
@@ -518,7 +519,7 @@ def get_range_info(response, get_headers, callback=_helpers.do_nothing):
518519
match = _CONTENT_RANGE_RE.match(content_range)
519520
if match is None:
520521
callback()
521-
raise common.InvalidResponse(
522+
raise InvalidResponse(
522523
response,
523524
"Unexpected content-range header",
524525
content_range,

google/cloud/storage/_media/_helpers.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
from urllib.parse import urlsplit
2828
from urllib.parse import urlunsplit
2929

30-
from google.resumable_media import common
30+
from google.cloud.storage._media import common
31+
from google.cloud.storage.exceptions import InvalidResponse
3132

3233

3334
RANGE_HEADER = "range"
@@ -70,15 +71,13 @@ def header_required(response, name, get_headers, callback=do_nothing):
7071
str: The desired header.
7172
7273
Raises:
73-
~google.resumable_media.common.InvalidResponse: If the header
74+
~google.cloud.storage.exceptions.InvalidResponse: If the header
7475
is missing.
7576
"""
7677
headers = get_headers(response)
7778
if name not in headers:
7879
callback()
79-
raise common.InvalidResponse(
80-
response, "Response headers must contain header", name
81-
)
80+
raise InvalidResponse(response, "Response headers must contain header", name)
8281

8382
return headers[name]
8483

@@ -98,14 +97,14 @@ def require_status_code(response, status_codes, get_status_code, callback=do_not
9897
int: The status code.
9998
10099
Raises:
101-
~google.resumable_media.common.InvalidResponse: If the status code
100+
~google.cloud.storage.exceptions.InvalidResponse: If the status code
102101
is not one of the values in ``status_codes``.
103102
"""
104103
status_code = get_status_code(response)
105104
if status_code not in status_codes:
106105
if status_code not in common.RETRYABLE:
107106
callback()
108-
raise common.InvalidResponse(
107+
raise InvalidResponse(
109108
response,
110109
"Request failed with status code",
111110
status_code,
@@ -298,7 +297,7 @@ def _parse_checksum_header(header_value, response, checksum_label):
298297
can be detected from the ``X-Goog-Hash`` header; otherwise, None.
299298
300299
Raises:
301-
~google.resumable_media.common.InvalidResponse: If there are
300+
~google.cloud.storage.exceptions.InvalidResponse: If there are
302301
multiple checksums of the requested type in ``header_value``.
303302
"""
304303
if header_value is None:
@@ -316,7 +315,7 @@ def _parse_checksum_header(header_value, response, checksum_label):
316315
elif len(matches) == 1:
317316
return matches[0]
318317
else:
319-
raise common.InvalidResponse(
318+
raise InvalidResponse(
320319
response,
321320
"X-Goog-Hash header had multiple ``{}`` values.".format(checksum_label),
322321
header_value,

0 commit comments

Comments
 (0)