Skip to content

Commit dd9dc2f

Browse files
pomegraniteddennisv
authored andcommitted
Adds option for lazy connection to swift (#97)
Instead of connecting to SWIFT on storage init, this adds an option to allow the SWIFT connection to be created only when required.
1 parent d6b52cf commit dd9dc2f

File tree

4 files changed

+60
-33
lines changed

4 files changed

+60
-33
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name='django-storage-swift',
5-
version='1.2.17',
5+
version='1.2.18',
66
description='OpenStack Swift storage backend for Django',
77
long_description=open('README.rst').read(),
88
url='https://github.com/dennisv/django-storage-swift',

swift/storage.py

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,12 @@ class SwiftStorage(Storage):
140140
auth_token_duration = setting('SWIFT_AUTH_TOKEN_DURATION', 60 * 60 * 23)
141141
os_extra_options = setting('SWIFT_EXTRA_OPTIONS', {})
142142
auto_overwrite = setting('SWIFT_AUTO_OVERWRITE', False)
143+
lazy_connect = setting('SWIFT_LAZY_CONNECT', False)
143144
content_type_from_fd = setting('SWIFT_CONTENT_TYPE_FROM_FD', False)
144145
_token_creation_time = 0
145146
_token = ''
147+
_swift_conn = None
148+
_base_url = None
146149
name_prefix = setting('SWIFT_NAME_PREFIX', '')
147150
full_listing = setting('SWIFT_FULL_LISTING', True)
148151
max_retries = setting('SWIFT_MAX_RETRIES', 5)
@@ -170,17 +173,28 @@ def __init__(self, **settings):
170173
}
171174
self.os_options.update(self.os_extra_options)
172175

173-
# Get Connection wrapper
174-
self.swift_conn = swiftclient.Connection(
175-
authurl=self.api_auth_url,
176-
user=self.api_username,
177-
key=self.api_key,
178-
retries=self.max_retries,
179-
tenant_name=self.tenant_name,
180-
os_options=self.os_options,
181-
auth_version=self.auth_version)
182-
183-
# Check container
176+
if not self.lazy_connect:
177+
self.swift_conn
178+
179+
@property
180+
def swift_conn(self):
181+
"""Get swift connection wrapper"""
182+
if not self._swift_conn:
183+
self._swift_conn = swiftclient.Connection(
184+
authurl=self.api_auth_url,
185+
user=self.api_username,
186+
key=self.api_key,
187+
retries=self.max_retries,
188+
tenant_name=self.tenant_name,
189+
os_options=self.os_options,
190+
auth_version=self.auth_version)
191+
self._check_container()
192+
return self._swift_conn
193+
194+
def _check_container(self):
195+
"""
196+
Check that container exists; raises exception if not.
197+
"""
184198
try:
185199
self.swift_conn.head_container(self.container_name)
186200
except swiftclient.ClientException:
@@ -197,26 +211,30 @@ def __init__(self, **settings):
197211
raise ImproperlyConfigured(
198212
"Container %s does not exist." % self.container_name)
199213

200-
if self.auto_base_url:
201-
# Derive a base URL based on the authentication information from
202-
# the server, optionally overriding the protocol, host/port and
203-
# potentially adding a path fragment before the auth information.
204-
self.base_url = self.swift_conn.url + '/'
205-
if self.override_base_url is not None:
206-
# override the protocol and host, append any path fragments
207-
split_derived = urlparse.urlsplit(self.base_url)
208-
split_override = urlparse.urlsplit(self.override_base_url)
209-
split_result = [''] * 5
210-
split_result[0:2] = split_override[0:2]
211-
split_result[2] = (split_override[2] + split_derived[2]
212-
).replace('//', '/')
213-
self.base_url = urlparse.urlunsplit(split_result)
214-
215-
self.base_url = urlparse.urljoin(self.base_url,
216-
self.container_name)
217-
self.base_url += '/'
218-
else:
219-
self.base_url = self.override_base_url
214+
@property
215+
def base_url(self):
216+
if self._base_url is None:
217+
if self.auto_base_url:
218+
# Derive a base URL based on the authentication information from
219+
# the server, optionally overriding the protocol, host/port and
220+
# potentially adding a path fragment before the auth information.
221+
self._base_url = self.swift_conn.url + '/'
222+
if self.override_base_url is not None:
223+
# override the protocol and host, append any path fragments
224+
split_derived = urlparse.urlsplit(self._base_url)
225+
split_override = urlparse.urlsplit(self.override_base_url)
226+
split_result = [''] * 5
227+
split_result[0:2] = split_override[0:2]
228+
split_result[2] = (split_override[2] + split_derived[2]
229+
).replace('//', '/')
230+
self._base_url = urlparse.urlunsplit(split_result)
231+
232+
self._base_url = urlparse.urljoin(self._base_url,
233+
self.container_name)
234+
self._base_url += '/'
235+
else:
236+
self._base_url = self.override_base_url
237+
return self._base_url
220238

221239
def _open(self, name, mode='rb'):
222240
original_name = name

tests/tests.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ def test_illegal_extra_opts(self):
159159
with self.assertRaises(ImproperlyConfigured):
160160
self.default_storage('v3', os_extra_options="boom!")
161161

162+
@patch.object(FakeSwift.Connection, '__init__', return_value=None)
163+
def test_override_lazy_connect(self, mock_swift_init):
164+
"""Test setting lazy_connect delays connection creation"""
165+
backend = self.default_storage('v3', lazy_connect=True)
166+
assert not mock_swift_init.called
167+
self.assertFalse(backend.exists('warez/some_random_movie.mp4'))
168+
assert mock_swift_init.called
169+
162170

163171
# @patch('swift.storage.swiftclient', new=FakeSwift)
164172
# class TokenTest(SwiftStorageTestCase):

tests/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,15 @@ class FakeSwift(object):
9191
containers = ['container']
9292

9393
class Connection(object):
94+
service_token = None
9495
def __init__(self, authurl=None, user=None, key=None, retries=5,
9596
preauthurl=None, preauthtoken=None, snet=False,
9697
starting_backoff=1, max_backoff=64, tenant_name=None,
9798
os_options=None, auth_version="1", cacert=None,
9899
insecure=False, cert=None, cert_key=None,
99100
ssl_compression=True, retry_on_ratelimit=False,
100101
timeout=None, session=None):
101-
self.service_token = None
102+
pass
102103

103104
def _retry(self, reset_func, func, *args, **kwargs):
104105
self.url, self.token = self.get_auth()

0 commit comments

Comments
 (0)