Added delete option to database storage.
This commit is contained in:
parent
308604a33c
commit
963b5bc68b
1868 changed files with 192402 additions and 13278 deletions
536
venv/Lib/site-packages/google/resumable_media/requests/upload.py
Normal file
536
venv/Lib/site-packages/google/resumable_media/requests/upload.py
Normal file
|
@ -0,0 +1,536 @@
|
|||
# Copyright 2017 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.
|
||||
|
||||
"""Support for resumable uploads.
|
||||
|
||||
Also supported here are simple (media) uploads and multipart
|
||||
uploads that contain both metadata and a small file as payload.
|
||||
"""
|
||||
|
||||
|
||||
from google.resumable_media import _upload
|
||||
from google.resumable_media.requests import _request_helpers
|
||||
|
||||
|
||||
class SimpleUpload(_request_helpers.RequestsMixin, _upload.SimpleUpload):
|
||||
"""Upload a resource to a Google API.
|
||||
|
||||
A **simple** media upload sends no metadata and completes the upload
|
||||
in a single request.
|
||||
|
||||
Args:
|
||||
upload_url (str): The URL where the content will be uploaded.
|
||||
headers (Optional[Mapping[str, str]]): Extra headers that should
|
||||
be sent with the request, e.g. headers for encrypted data.
|
||||
|
||||
Attributes:
|
||||
upload_url (str): The URL where the content will be uploaded.
|
||||
"""
|
||||
|
||||
def transmit(
|
||||
self,
|
||||
transport,
|
||||
data,
|
||||
content_type,
|
||||
timeout=(
|
||||
_request_helpers._DEFAULT_CONNECT_TIMEOUT,
|
||||
_request_helpers._DEFAULT_READ_TIMEOUT,
|
||||
),
|
||||
):
|
||||
"""Transmit the resource to be uploaded.
|
||||
|
||||
Args:
|
||||
transport (~requests.Session): A ``requests`` object which can
|
||||
make authenticated requests.
|
||||
data (bytes): The resource content to be uploaded.
|
||||
content_type (str): The content type of the resource, e.g. a JPEG
|
||||
image has content type ``image/jpeg``.
|
||||
timeout (Optional[Union[float, Tuple[float, float]]]):
|
||||
The number of seconds to wait for the server response.
|
||||
Depending on the retry strategy, a request may be repeated
|
||||
several times using the same timeout each time.
|
||||
|
||||
Can also be passed as a tuple (connect_timeout, read_timeout).
|
||||
See :meth:`requests.Session.request` documentation for details.
|
||||
|
||||
Returns:
|
||||
~requests.Response: The HTTP response returned by ``transport``.
|
||||
"""
|
||||
method, url, payload, headers = self._prepare_request(data, content_type)
|
||||
response = _request_helpers.http_request(
|
||||
transport,
|
||||
method,
|
||||
url,
|
||||
data=payload,
|
||||
headers=headers,
|
||||
retry_strategy=self._retry_strategy,
|
||||
timeout=timeout,
|
||||
)
|
||||
self._process_response(response)
|
||||
return response
|
||||
|
||||
|
||||
class MultipartUpload(_request_helpers.RequestsMixin, _upload.MultipartUpload):
|
||||
"""Upload a resource with metadata to a Google API.
|
||||
|
||||
A **multipart** upload sends both metadata and the resource in a single
|
||||
(multipart) request.
|
||||
|
||||
Args:
|
||||
upload_url (str): The URL where the content will be uploaded.
|
||||
headers (Optional[Mapping[str, str]]): Extra headers that should
|
||||
be sent with the request, e.g. headers for encrypted data.
|
||||
checksum Optional([str]): The type of checksum to compute to verify
|
||||
the integrity of the object. The request metadata will be amended
|
||||
to include the computed value. Using this option will override a
|
||||
manually-set checksum value. Supported values are "md5",
|
||||
"crc32c" and None. The default is None.
|
||||
|
||||
Attributes:
|
||||
upload_url (str): The URL where the content will be uploaded.
|
||||
"""
|
||||
|
||||
def transmit(
|
||||
self,
|
||||
transport,
|
||||
data,
|
||||
metadata,
|
||||
content_type,
|
||||
timeout=(
|
||||
_request_helpers._DEFAULT_CONNECT_TIMEOUT,
|
||||
_request_helpers._DEFAULT_READ_TIMEOUT,
|
||||
),
|
||||
):
|
||||
"""Transmit the resource to be uploaded.
|
||||
|
||||
Args:
|
||||
transport (~requests.Session): A ``requests`` object which can
|
||||
make authenticated requests.
|
||||
data (bytes): The resource content to be uploaded.
|
||||
metadata (Mapping[str, str]): The resource metadata, such as an
|
||||
ACL list.
|
||||
content_type (str): The content type of the resource, e.g. a JPEG
|
||||
image has content type ``image/jpeg``.
|
||||
timeout (Optional[Union[float, Tuple[float, float]]]):
|
||||
The number of seconds to wait for the server response.
|
||||
Depending on the retry strategy, a request may be repeated
|
||||
several times using the same timeout each time.
|
||||
|
||||
Can also be passed as a tuple (connect_timeout, read_timeout).
|
||||
See :meth:`requests.Session.request` documentation for details.
|
||||
|
||||
Returns:
|
||||
~requests.Response: The HTTP response returned by ``transport``.
|
||||
"""
|
||||
method, url, payload, headers = self._prepare_request(
|
||||
data, metadata, content_type
|
||||
)
|
||||
response = _request_helpers.http_request(
|
||||
transport,
|
||||
method,
|
||||
url,
|
||||
data=payload,
|
||||
headers=headers,
|
||||
retry_strategy=self._retry_strategy,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
self._process_response(response)
|
||||
return response
|
||||
|
||||
|
||||
class ResumableUpload(_request_helpers.RequestsMixin, _upload.ResumableUpload):
|
||||
"""Initiate and fulfill a resumable upload to a Google API.
|
||||
|
||||
A **resumable** upload sends an initial request with the resource metadata
|
||||
and then gets assigned an upload ID / upload URL to send bytes to.
|
||||
Using the upload URL, the upload is then done in chunks (determined by
|
||||
the user) until all bytes have been uploaded.
|
||||
|
||||
When constructing a resumable upload, only the resumable upload URL and
|
||||
the chunk size are required:
|
||||
|
||||
.. testsetup:: resumable-constructor
|
||||
|
||||
bucket = u'bucket-foo'
|
||||
|
||||
.. doctest:: resumable-constructor
|
||||
|
||||
>>> from google.resumable_media.requests import ResumableUpload
|
||||
>>>
|
||||
>>> url_template = (
|
||||
... u'https://www.googleapis.com/upload/storage/v1/b/{bucket}/o?'
|
||||
... u'uploadType=resumable')
|
||||
>>> upload_url = url_template.format(bucket=bucket)
|
||||
>>>
|
||||
>>> chunk_size = 3 * 1024 * 1024 # 3MB
|
||||
>>> upload = ResumableUpload(upload_url, chunk_size)
|
||||
|
||||
When initiating an upload (via :meth:`initiate`), the caller is expected
|
||||
to pass the resource being uploaded as a file-like ``stream``. If the size
|
||||
of the resource is explicitly known, it can be passed in directly:
|
||||
|
||||
.. testsetup:: resumable-explicit-size
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import mock
|
||||
import requests
|
||||
from six.moves import http_client
|
||||
|
||||
from google.resumable_media.requests import ResumableUpload
|
||||
|
||||
upload_url = u'http://test.invalid'
|
||||
chunk_size = 3 * 1024 * 1024 # 3MB
|
||||
upload = ResumableUpload(upload_url, chunk_size)
|
||||
|
||||
file_desc, filename = tempfile.mkstemp()
|
||||
os.close(file_desc)
|
||||
|
||||
data = b'some bytes!'
|
||||
with open(filename, u'wb') as file_obj:
|
||||
file_obj.write(data)
|
||||
|
||||
fake_response = requests.Response()
|
||||
fake_response.status_code = int(http_client.OK)
|
||||
fake_response._content = b''
|
||||
resumable_url = u'http://test.invalid?upload_id=7up'
|
||||
fake_response.headers[u'location'] = resumable_url
|
||||
|
||||
post_method = mock.Mock(return_value=fake_response, spec=[])
|
||||
transport = mock.Mock(request=post_method, spec=['request'])
|
||||
|
||||
.. doctest:: resumable-explicit-size
|
||||
|
||||
>>> import os
|
||||
>>>
|
||||
>>> upload.total_bytes is None
|
||||
True
|
||||
>>>
|
||||
>>> stream = open(filename, u'rb')
|
||||
>>> total_bytes = os.path.getsize(filename)
|
||||
>>> metadata = {u'name': filename}
|
||||
>>> response = upload.initiate(
|
||||
... transport, stream, metadata, u'text/plain',
|
||||
... total_bytes=total_bytes)
|
||||
>>> response
|
||||
<Response [200]>
|
||||
>>>
|
||||
>>> upload.total_bytes == total_bytes
|
||||
True
|
||||
|
||||
.. testcleanup:: resumable-explicit-size
|
||||
|
||||
os.remove(filename)
|
||||
|
||||
If the stream is in a "final" state (i.e. it won't have any more bytes
|
||||
written to it), the total number of bytes can be determined implicitly
|
||||
from the ``stream`` itself:
|
||||
|
||||
.. testsetup:: resumable-implicit-size
|
||||
|
||||
import io
|
||||
|
||||
import mock
|
||||
import requests
|
||||
from six.moves import http_client
|
||||
|
||||
from google.resumable_media.requests import ResumableUpload
|
||||
|
||||
upload_url = u'http://test.invalid'
|
||||
chunk_size = 3 * 1024 * 1024 # 3MB
|
||||
upload = ResumableUpload(upload_url, chunk_size)
|
||||
|
||||
fake_response = requests.Response()
|
||||
fake_response.status_code = int(http_client.OK)
|
||||
fake_response._content = b''
|
||||
resumable_url = u'http://test.invalid?upload_id=7up'
|
||||
fake_response.headers[u'location'] = resumable_url
|
||||
|
||||
post_method = mock.Mock(return_value=fake_response, spec=[])
|
||||
transport = mock.Mock(request=post_method, spec=['request'])
|
||||
|
||||
data = b'some MOAR bytes!'
|
||||
metadata = {u'name': u'some-file.jpg'}
|
||||
content_type = u'image/jpeg'
|
||||
|
||||
.. doctest:: resumable-implicit-size
|
||||
|
||||
>>> stream = io.BytesIO(data)
|
||||
>>> response = upload.initiate(
|
||||
... transport, stream, metadata, content_type)
|
||||
>>>
|
||||
>>> upload.total_bytes == len(data)
|
||||
True
|
||||
|
||||
If the size of the resource is **unknown** when the upload is initiated,
|
||||
the ``stream_final`` argument can be used. This might occur if the
|
||||
resource is being dynamically created on the client (e.g. application
|
||||
logs). To use this argument:
|
||||
|
||||
.. testsetup:: resumable-unknown-size
|
||||
|
||||
import io
|
||||
|
||||
import mock
|
||||
import requests
|
||||
from six.moves import http_client
|
||||
|
||||
from google.resumable_media.requests import ResumableUpload
|
||||
|
||||
upload_url = u'http://test.invalid'
|
||||
chunk_size = 3 * 1024 * 1024 # 3MB
|
||||
upload = ResumableUpload(upload_url, chunk_size)
|
||||
|
||||
fake_response = requests.Response()
|
||||
fake_response.status_code = int(http_client.OK)
|
||||
fake_response._content = b''
|
||||
resumable_url = u'http://test.invalid?upload_id=7up'
|
||||
fake_response.headers[u'location'] = resumable_url
|
||||
|
||||
post_method = mock.Mock(return_value=fake_response, spec=[])
|
||||
transport = mock.Mock(request=post_method, spec=['request'])
|
||||
|
||||
metadata = {u'name': u'some-file.jpg'}
|
||||
content_type = u'application/octet-stream'
|
||||
|
||||
stream = io.BytesIO(b'data')
|
||||
|
||||
.. doctest:: resumable-unknown-size
|
||||
|
||||
>>> response = upload.initiate(
|
||||
... transport, stream, metadata, content_type,
|
||||
... stream_final=False)
|
||||
>>>
|
||||
>>> upload.total_bytes is None
|
||||
True
|
||||
|
||||
Args:
|
||||
upload_url (str): The URL where the resumable upload will be initiated.
|
||||
chunk_size (int): The size of each chunk used to upload the resource.
|
||||
headers (Optional[Mapping[str, str]]): Extra headers that should
|
||||
be sent with the :meth:`initiate` request, e.g. headers for
|
||||
encrypted data. These **will not** be sent with
|
||||
:meth:`transmit_next_chunk` or :meth:`recover` requests.
|
||||
checksum Optional([str]): The type of checksum to compute to verify
|
||||
the integrity of the object. After the upload is complete, the
|
||||
server-computed checksum of the resulting object will be checked
|
||||
and google.resumable_media.common.DataCorruption will be raised on
|
||||
a mismatch. The corrupted file will not be deleted from the remote
|
||||
host automatically. Supported values are "md5", "crc32c" and None.
|
||||
The default is None.
|
||||
|
||||
Attributes:
|
||||
upload_url (str): The URL where the content will be uploaded.
|
||||
|
||||
Raises:
|
||||
ValueError: If ``chunk_size`` is not a multiple of
|
||||
:data:`.UPLOAD_CHUNK_SIZE`.
|
||||
"""
|
||||
|
||||
def initiate(
|
||||
self,
|
||||
transport,
|
||||
stream,
|
||||
metadata,
|
||||
content_type,
|
||||
total_bytes=None,
|
||||
stream_final=True,
|
||||
timeout=(
|
||||
_request_helpers._DEFAULT_CONNECT_TIMEOUT,
|
||||
_request_helpers._DEFAULT_READ_TIMEOUT,
|
||||
),
|
||||
):
|
||||
"""Initiate a resumable upload.
|
||||
|
||||
By default, this method assumes your ``stream`` is in a "final"
|
||||
state ready to transmit. However, ``stream_final=False`` can be used
|
||||
to indicate that the size of the resource is not known. This can happen
|
||||
if bytes are being dynamically fed into ``stream``, e.g. if the stream
|
||||
is attached to application logs.
|
||||
|
||||
If ``stream_final=False`` is used, :attr:`chunk_size` bytes will be
|
||||
read from the stream every time :meth:`transmit_next_chunk` is called.
|
||||
If one of those reads produces strictly fewer bites than the chunk
|
||||
size, the upload will be concluded.
|
||||
|
||||
Args:
|
||||
transport (~requests.Session): A ``requests`` object which can
|
||||
make authenticated requests.
|
||||
stream (IO[bytes]): The stream (i.e. file-like object) that will
|
||||
be uploaded. The stream **must** be at the beginning (i.e.
|
||||
``stream.tell() == 0``).
|
||||
metadata (Mapping[str, str]): The resource metadata, such as an
|
||||
ACL list.
|
||||
content_type (str): The content type of the resource, e.g. a JPEG
|
||||
image has content type ``image/jpeg``.
|
||||
total_bytes (Optional[int]): The total number of bytes to be
|
||||
uploaded. If specified, the upload size **will not** be
|
||||
determined from the stream (even if ``stream_final=True``).
|
||||
stream_final (Optional[bool]): Indicates if the ``stream`` is
|
||||
"final" (i.e. no more bytes will be added to it). In this case
|
||||
we determine the upload size from the size of the stream. If
|
||||
``total_bytes`` is passed, this argument will be ignored.
|
||||
timeout (Optional[Union[float, Tuple[float, float]]]):
|
||||
The number of seconds to wait for the server response.
|
||||
Depending on the retry strategy, a request may be repeated
|
||||
several times using the same timeout each time.
|
||||
|
||||
Can also be passed as a tuple (connect_timeout, read_timeout).
|
||||
See :meth:`requests.Session.request` documentation for details.
|
||||
|
||||
Returns:
|
||||
~requests.Response: The HTTP response returned by ``transport``.
|
||||
"""
|
||||
method, url, payload, headers = self._prepare_initiate_request(
|
||||
stream,
|
||||
metadata,
|
||||
content_type,
|
||||
total_bytes=total_bytes,
|
||||
stream_final=stream_final,
|
||||
)
|
||||
response = _request_helpers.http_request(
|
||||
transport,
|
||||
method,
|
||||
url,
|
||||
data=payload,
|
||||
headers=headers,
|
||||
retry_strategy=self._retry_strategy,
|
||||
timeout=timeout,
|
||||
)
|
||||
self._process_initiate_response(response)
|
||||
return response
|
||||
|
||||
def transmit_next_chunk(
|
||||
self,
|
||||
transport,
|
||||
timeout=(
|
||||
_request_helpers._DEFAULT_CONNECT_TIMEOUT,
|
||||
_request_helpers._DEFAULT_READ_TIMEOUT,
|
||||
),
|
||||
):
|
||||
"""Transmit the next chunk of the resource to be uploaded.
|
||||
|
||||
If the current upload was initiated with ``stream_final=False``,
|
||||
this method will dynamically determine if the upload has completed.
|
||||
The upload will be considered complete if the stream produces
|
||||
fewer than :attr:`chunk_size` bytes when a chunk is read from it.
|
||||
|
||||
In the case of failure, an exception is thrown that preserves the
|
||||
failed response:
|
||||
|
||||
.. testsetup:: bad-response
|
||||
|
||||
import io
|
||||
|
||||
import mock
|
||||
import requests
|
||||
from six.moves import http_client
|
||||
|
||||
from google import resumable_media
|
||||
import google.resumable_media.requests.upload as upload_mod
|
||||
|
||||
transport = mock.Mock(spec=['request'])
|
||||
fake_response = requests.Response()
|
||||
fake_response.status_code = int(http_client.BAD_REQUEST)
|
||||
transport.request.return_value = fake_response
|
||||
|
||||
upload_url = u'http://test.invalid'
|
||||
upload = upload_mod.ResumableUpload(
|
||||
upload_url, resumable_media.UPLOAD_CHUNK_SIZE)
|
||||
# Fake that the upload has been initiate()-d
|
||||
data = b'data is here'
|
||||
upload._stream = io.BytesIO(data)
|
||||
upload._total_bytes = len(data)
|
||||
upload._resumable_url = u'http://test.invalid?upload_id=nope'
|
||||
|
||||
.. doctest:: bad-response
|
||||
:options: +NORMALIZE_WHITESPACE
|
||||
|
||||
>>> error = None
|
||||
>>> try:
|
||||
... upload.transmit_next_chunk(transport)
|
||||
... except resumable_media.InvalidResponse as caught_exc:
|
||||
... error = caught_exc
|
||||
...
|
||||
>>> error
|
||||
InvalidResponse('Request failed with status code', 400,
|
||||
'Expected one of', <HTTPStatus.OK: 200>, 308)
|
||||
>>> error.response
|
||||
<Response [400]>
|
||||
|
||||
Args:
|
||||
transport (~requests.Session): A ``requests`` object which can
|
||||
make authenticated requests.
|
||||
timeout (Optional[Union[float, Tuple[float, float]]]):
|
||||
The number of seconds to wait for the server response.
|
||||
Depending on the retry strategy, a request may be repeated
|
||||
several times using the same timeout each time.
|
||||
|
||||
Can also be passed as a tuple (connect_timeout, read_timeout).
|
||||
See :meth:`requests.Session.request` documentation for details.
|
||||
|
||||
Returns:
|
||||
~requests.Response: The HTTP response returned by ``transport``.
|
||||
|
||||
Raises:
|
||||
~google.resumable_media.common.InvalidResponse: If the status
|
||||
code is not 200 or 308.
|
||||
~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.
|
||||
"""
|
||||
method, url, payload, headers = self._prepare_request()
|
||||
response = _request_helpers.http_request(
|
||||
transport,
|
||||
method,
|
||||
url,
|
||||
data=payload,
|
||||
headers=headers,
|
||||
retry_strategy=self._retry_strategy,
|
||||
timeout=timeout,
|
||||
)
|
||||
self._process_response(response, len(payload))
|
||||
return response
|
||||
|
||||
def recover(self, transport):
|
||||
"""Recover from a failure.
|
||||
|
||||
This method should be used when a :class:`ResumableUpload` is in an
|
||||
:attr:`~ResumableUpload.invalid` state due to a request failure.
|
||||
|
||||
This will verify the progress with the server and make sure the
|
||||
current upload is in a valid state before :meth:`transmit_next_chunk`
|
||||
can be used again.
|
||||
|
||||
Args:
|
||||
transport (~requests.Session): A ``requests`` object which can
|
||||
make authenticated requests.
|
||||
|
||||
Returns:
|
||||
~requests.Response: The HTTP response returned by ``transport``.
|
||||
"""
|
||||
method, url, payload, headers = self._prepare_recover_request()
|
||||
# NOTE: We assume "payload is None" but pass it along anyway.
|
||||
response = _request_helpers.http_request(
|
||||
transport,
|
||||
method,
|
||||
url,
|
||||
data=payload,
|
||||
headers=headers,
|
||||
retry_strategy=self._retry_strategy,
|
||||
)
|
||||
self._process_recover_response(response)
|
||||
return response
|
Loading…
Add table
Add a link
Reference in a new issue