Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gcloud] fix saving content as gzipped #1203

Merged
merged 1 commit into from Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 15 additions & 8 deletions storages/backends/gcloud.py
@@ -1,3 +1,5 @@
import gzip
import io
import mimetypes
import warnings
from datetime import timedelta
Expand All @@ -11,11 +13,9 @@

from storages.base import BaseStorage
from storages.compress import CompressedFileMixin
from storages.compress import CompressStorageMixin
from storages.utils import check_location
from storages.utils import clean_name
from storages.utils import get_available_overwrite_name
from storages.utils import is_seekable
from storages.utils import safe_join
from storages.utils import setting
from storages.utils import to_bytes
Expand Down Expand Up @@ -102,7 +102,7 @@ def close(self):


@deconstructible
class GoogleCloudStorage(CompressStorageMixin, BaseStorage):
class GoogleCloudStorage(BaseStorage):
def __init__(self, **settings):
super().__init__(**settings)

Expand Down Expand Up @@ -174,6 +174,14 @@ def _open(self, name, mode='rb'):
raise FileNotFoundError('File does not exist: %s' % name)
return file_object

def _compress_content(self, content):
content.seek(0)
zbuf = io.BytesIO()
with gzip.GzipFile(mode='wb', fileobj=zbuf, mtime=0.0) as zfile:
zfile.write(to_bytes(content.read()))
zbuf.seek(0)
return zbuf

def _save(self, name, content):
cleaned_name = clean_name(name)
name = self._normalize_name(cleaned_name)
Expand All @@ -195,10 +203,9 @@ def _save(self, name, content):
for prop, val in blob_params.items():
setattr(file_object.blob, prop, val)

rewind = is_seekable(content)
file_object.blob.upload_from_file(
content,
rewind=rewind,
rewind=True,
retry=DEFAULT_RETRY,
size=getattr(content, 'size', None),
**upload_params
Expand Down Expand Up @@ -300,9 +307,9 @@ def get_created_time(self, name):

def url(self, name, parameters=None):
"""
Return public url or a signed url for the Blob.
This DOES NOT check for existance of Blob - that makes codes too slow
for many use cases.
Return public URL or a signed URL for the Blob.

The existnce of blobs are not verified for public URLs, it makes the code too slow.
"""
name = self._normalize_name(clean_name(name))
blob = self.bucket.blob(name)
Expand Down
41 changes: 5 additions & 36 deletions tests/test_gcloud.py
Expand Up @@ -14,7 +14,6 @@
from google.cloud.storage.retry import DEFAULT_RETRY

from storages.backends import gcloud
from tests.utils import NonSeekableContentFile


class GCloudTestCase(TestCase):
Expand Down Expand Up @@ -417,9 +416,7 @@ def test_cache_control(self):
self.assertEqual(blob.cache_control, cache_control)

def test_storage_save_gzip_twice(self):
"""
Test saving the same file content twice with gzip enabled.
"""
"""Test saving the same file content twice with gzip enabled."""
# Given
self.storage.gzip = True
name = 'test_storage_save.css'
Expand All @@ -435,7 +432,7 @@ def test_storage_save_gzip_twice(self):
self.assertEqual(obj.content_encoding, 'gzip')
obj.upload_from_file.assert_called_with(
mock.ANY,
rewind=False,
rewind=True,
retry=DEFAULT_RETRY,
size=None,
predefined_acl=None,
Expand All @@ -447,9 +444,7 @@ def test_storage_save_gzip_twice(self):
self.assertEqual(zfile.read(), b"I should be gzip'd")

def test_compress_content_len(self):
"""
Test that file returned by _compress_content() is readable.
"""
"""Test that file returned by _compress_content() is readable."""
self.storage.gzip = True
content = ContentFile("I should be gzip'd")
content = self.storage._compress_content(content)
Expand Down Expand Up @@ -532,7 +527,7 @@ def test_storage_save_gzipped(self, *args):
self.storage.save(name, content)
blob.upload_from_file.assert_called_with(
mock.ANY,
rewind=False,
rewind=True,
retry=DEFAULT_RETRY,
size=None,
predefined_acl=None,
Expand All @@ -541,32 +536,6 @@ def test_storage_save_gzipped(self, *args):
finally:
patcher.stop()

@mock.patch('google.cloud.storage.blob.Blob._do_upload')
@mock.patch('google.auth.default', return_value=['foo', None])
def test_storage_save_gzipped_non_seekable(self, *args):
"""
Test saving a gzipped file
"""
name = 'test_storage_save.gz'
content = NonSeekableContentFile("I am gzip'd")

blob = Blob('x', None)
blob.upload_from_file = mock.MagicMock(side_effect=blob.upload_from_file)
patcher = mock.patch('google.cloud.storage.Bucket.get_blob', return_value=blob)
try:
patcher.start()
self.storage.save(name, content)
blob.upload_from_file.assert_called_with(
mock.ANY,
rewind=False,
retry=DEFAULT_RETRY,
size=11,
predefined_acl=None,
content_type=None
)
finally:
patcher.stop()

@mock.patch('google.cloud.storage.blob.Blob._do_upload')
@mock.patch('google.auth.default', return_value=['foo', None])
def test_storage_save_gzip(self, *args):
Expand All @@ -587,7 +556,7 @@ def test_storage_save_gzip(self, *args):
obj = self.storage._bucket.get_blob()
obj.upload_from_file.assert_called_with(
mock.ANY,
rewind=False,
rewind=True,
retry=DEFAULT_RETRY,
size=None,
predefined_acl=None,
Expand Down