Skip to content

Commit

Permalink
Fixed #550 -- S3Boto3Storage is now pickleable
Browse files Browse the repository at this point in the history
  • Loading branch information
sbneto authored and jschneier committed Aug 12, 2018
1 parent 776358e commit 82e18aa
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
11 changes: 11 additions & 0 deletions storages/backends/s3boto3.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,17 @@ def __init__(self, acl=None, bucket=None, **settings):
"set AWS_DEFAULT_ACL."
)

def __getstate__(self):
state = self.__dict__.copy()
state.pop('_connections', None)
state.pop('_bucket', None)
return state

def __setstate__(self, state):
state['_connections'] = threading.local()
state['_bucket'] = None
self.__dict__ = state

@property
def connection(self):
# TODO: Support host, port like in s3boto
Expand Down
32 changes: 32 additions & 0 deletions tests/test_s3boto3.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import unicode_literals

import gzip
import pickle
import threading
import warnings
from datetime import datetime
Expand Down Expand Up @@ -59,6 +60,37 @@ def test_clean_name_windows(self):
path = self.storage._clean_name("path\\to\\somewhere")
self.assertEqual(path, "path/to/somewhere")

def test_pickle_with_bucket(self):
"""
Test that the storage can be pickled with a bucket attached
"""
# Ensure the bucket has been used
self.storage.bucket
self.assertIsNotNone(self.storage._bucket)

# Can't pickle MagicMock, but you can't pickle a real Bucket object either
p = pickle.dumps(self.storage)
new_storage = pickle.loads(p)

self.assertIsInstance(new_storage._connections, threading.local)
# Put the mock connection back in
new_storage._connections.connection = mock.MagicMock()

self.assertIsNone(new_storage._bucket)
new_storage.bucket
self.assertIsNotNone(new_storage._bucket)

def test_pickle_without_bucket(self):
"""
Test that the storage can be pickled, without a bucket instance
"""

# Can't pickle a threadlocal
p = pickle.dumps(self.storage)
new_storage = pickle.loads(p)

self.assertIsInstance(new_storage._connections, threading.local)

def test_storage_url_slashes(self):
"""
Test URL generation.
Expand Down

0 comments on commit 82e18aa

Please sign in to comment.