summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Seger <Mark.Seger@hp.com>2013-06-12 12:02:02 +0000
committerMark Seger <Mark.Seger@hp.com>2013-06-20 11:51:08 +0000
commit3f66a8ae6b8d9741809773a9665d4768d2f161a6 (patch)
tree4d9e20654e3ed17eb14015e8bc33a8786f906b31
parent5d84ff8d9b74997d4494b7bed986f99cfceda9ad (diff)
downloadpython-swiftclient-3f66a8ae6b8d9741809773a9665d4768d2f161a6.tar.gz
add optional 'response_dict' parameters to many calls into which they'll
return a dictionary of the response status, reason and headers Change-Id: I35f8824537f0484362dd6646c91789fac02fa075
-rw-r--r--swiftclient/client.py149
1 files changed, 120 insertions, 29 deletions
diff --git a/swiftclient/client.py b/swiftclient/client.py
index 3d848cd..e95819f 100644
--- a/swiftclient/client.py
+++ b/swiftclient/client.py
@@ -332,6 +332,25 @@ def get_auth(auth_url, user, key, **kwargs):
% auth_version)
+def store_response(resp, response_dict):
+ """
+ store information about an operation into a dict
+
+ :param resp: an http response object containing the response
+ headers
+ :param response_dict: a dict into which are placed the
+ status, reason and a dict of lower-cased headers
+ """
+ if response_dict is not None:
+ resp_headers = {}
+ for header, value in resp.getheaders():
+ resp_headers[header.lower()] = value
+
+ response_dict['status'] = resp.status
+ response_dict['reason'] = resp.reason
+ response_dict['headers'] = resp_headers
+
+
def get_account(url, token, marker=None, limit=None, prefix=None,
end_marker=None, http_conn=None, full_listing=False):
"""
@@ -432,7 +451,7 @@ def head_account(url, token, http_conn=None):
return resp_headers
-def post_account(url, token, headers, http_conn=None):
+def post_account(url, token, headers, http_conn=None, response_dict=None):
"""
Update an account's metadata.
@@ -441,6 +460,8 @@ def post_account(url, token, headers, http_conn=None):
:param headers: additional headers to include in the request
:param http_conn: HTTP connection object (If None, it will create the
conn object)
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
:raises ClientException: HTTP POST request failed
"""
if http_conn:
@@ -453,6 +474,9 @@ def post_account(url, token, headers, http_conn=None):
resp = conn.getresponse()
body = resp.read()
http_log((url, method,), {'headers': headers}, resp, body)
+
+ store_response(resp, response_dict)
+
if resp.status < 200 or resp.status >= 300:
raise ClientException('Account POST failed',
http_scheme=parsed.scheme,
@@ -582,7 +606,8 @@ def head_container(url, token, container, http_conn=None, headers=None):
return resp_headers
-def put_container(url, token, container, headers=None, http_conn=None):
+def put_container(url, token, container, headers=None, http_conn=None,
+ response_dict=None):
"""
Create a container
@@ -592,6 +617,8 @@ def put_container(url, token, container, headers=None, http_conn=None):
:param headers: additional headers to include in the request
:param http_conn: HTTP connection object (If None, it will create the
conn object)
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
:raises ClientException: HTTP PUT request failed
"""
if http_conn:
@@ -608,6 +635,9 @@ def put_container(url, token, container, headers=None, http_conn=None):
conn.request(method, path, '', headers)
resp = conn.getresponse()
body = resp.read()
+
+ store_response(resp, response_dict)
+
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
{'headers': headers}, resp, body)
if resp.status < 200 or resp.status >= 300:
@@ -618,7 +648,8 @@ def put_container(url, token, container, headers=None, http_conn=None):
http_response_content=body)
-def post_container(url, token, container, headers, http_conn=None):
+def post_container(url, token, container, headers, http_conn=None,
+ response_dict=None):
"""
Update a container's metadata.
@@ -628,6 +659,8 @@ def post_container(url, token, container, headers, http_conn=None):
:param headers: additional headers to include in the request
:param http_conn: HTTP connection object (If None, it will create the
conn object)
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
:raises ClientException: HTTP POST request failed
"""
if http_conn:
@@ -644,6 +677,9 @@ def post_container(url, token, container, headers, http_conn=None):
body = resp.read()
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
{'headers': headers}, resp, body)
+
+ store_response(resp, response_dict)
+
if resp.status < 200 or resp.status >= 300:
raise ClientException('Container POST failed',
http_scheme=parsed.scheme, http_host=conn.host,
@@ -652,7 +688,8 @@ def post_container(url, token, container, headers, http_conn=None):
http_response_content=body)
-def delete_container(url, token, container, http_conn=None):
+def delete_container(url, token, container, http_conn=None,
+ response_dict=None):
"""
Delete a container
@@ -661,6 +698,8 @@ def delete_container(url, token, container, http_conn=None):
:param container: container name to delete
:param http_conn: HTTP connection object (If None, it will create the
conn object)
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
:raises ClientException: HTTP DELETE request failed
"""
if http_conn:
@@ -675,6 +714,9 @@ def delete_container(url, token, container, http_conn=None):
body = resp.read()
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
{'headers': headers}, resp, body)
+
+ store_response(resp, response_dict)
+
if resp.status < 200 or resp.status >= 300:
raise ClientException('Container DELETE failed',
http_scheme=parsed.scheme, http_host=conn.host,
@@ -684,7 +726,8 @@ def delete_container(url, token, container, http_conn=None):
def get_object(url, token, container, name, http_conn=None,
- resp_chunk_size=None, query_string=None):
+ resp_chunk_size=None, query_string=None,
+ response_dict=None):
"""
Get an object
@@ -699,6 +742,8 @@ def get_object(url, token, container, name, http_conn=None,
the object's contents before making another
request.
:param query_string: if set will be appended with '?' to generated path
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
:returns: a tuple of (response headers, the object's contents) The response
headers will be a dict and all header names will be lowercase.
:raises ClientException: HTTP GET request failed
@@ -714,6 +759,12 @@ def get_object(url, token, container, name, http_conn=None,
headers = {'X-Auth-Token': token}
conn.request(method, path, '', headers)
resp = conn.getresponse()
+
+ parsed_response = {}
+ store_response(resp, parsed_response)
+ if response_dict is not None:
+ response_dict.update(parsed_response)
+
if resp.status < 200 or resp.status >= 300:
body = resp.read()
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
@@ -733,12 +784,10 @@ def get_object(url, token, container, name, http_conn=None,
object_body = _object_body()
else:
object_body = resp.read()
- resp_headers = {}
- for header, value in resp.getheaders():
- resp_headers[header.lower()] = value
http_log(('%s%s' % (url.replace(parsed.path, ''), path), method,),
{'headers': headers}, resp, None)
- return resp_headers, object_body
+
+ return parsed_response['headers'], object_body
def head_object(url, token, container, name, http_conn=None):
@@ -782,7 +831,7 @@ def head_object(url, token, container, name, http_conn=None):
def put_object(url, token=None, container=None, name=None, contents=None,
content_length=None, etag=None, chunk_size=None,
content_type=None, headers=None, http_conn=None, proxy=None,
- query_string=None):
+ query_string=None, response_dict=None):
"""
Put an object
@@ -811,7 +860,9 @@ def put_object(url, token=None, container=None, name=None, contents=None,
:param proxy: proxy to connect through, if any; None by default; str of the
format 'http://127.0.0.1:8888' to set one
:param query_string: if set will be appended with '?' to generated path
- :returns: etag from server response
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
+ :returns: etag
:raises ClientException: HTTP PUT request failed
"""
if http_conn:
@@ -878,16 +929,21 @@ def put_object(url, token=None, container=None, name=None, contents=None,
headers = {'X-Auth-Token': token}
http_log(('%s%s' % (url.replace(parsed.path, ''), path), 'PUT',),
{'headers': headers}, resp, body)
+
+ store_response(resp, response_dict)
+
if resp.status < 200 or resp.status >= 300:
raise ClientException('Object PUT failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port,
http_path=path, http_status=resp.status,
http_reason=resp.reason,
http_response_content=body)
+
return resp.getheader('etag', '').strip('"')
-def post_object(url, token, container, name, headers, http_conn=None):
+def post_object(url, token, container, name, headers, http_conn=None,
+ response_dict=None):
"""
Update object metadata
@@ -898,6 +954,8 @@ def post_object(url, token, container, name, headers, http_conn=None):
:param headers: additional headers to include in the request
:param http_conn: HTTP connection object (If None, it will create the
conn object)
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
:raises ClientException: HTTP POST request failed
"""
if http_conn:
@@ -911,6 +969,9 @@ def post_object(url, token, container, name, headers, http_conn=None):
body = resp.read()
http_log(('%s%s' % (url.replace(parsed.path, ''), path), 'POST',),
{'headers': headers}, resp, body)
+
+ store_response(resp, response_dict)
+
if resp.status < 200 or resp.status >= 300:
raise ClientException('Object POST failed', http_scheme=parsed.scheme,
http_host=conn.host, http_port=conn.port,
@@ -920,7 +981,8 @@ def post_object(url, token, container, name, headers, http_conn=None):
def delete_object(url, token=None, container=None, name=None, http_conn=None,
- headers=None, proxy=None, query_string=None):
+ headers=None, proxy=None, query_string=None,
+ response_dict=None):
"""
Delete object
@@ -936,6 +998,8 @@ def delete_object(url, token=None, container=None, name=None, http_conn=None,
:param proxy: proxy to connect through, if any; None by default; str of the
format 'http://127.0.0.1:8888' to set one
:param query_string: if set will be appended with '?' to generated path
+ :param response_dict: an optional dictionary into which to place
+ the response - status, reason and headers
:raises ClientException: HTTP DELETE request failed
"""
if http_conn:
@@ -960,6 +1024,9 @@ def delete_object(url, token=None, container=None, name=None, http_conn=None,
body = resp.read()
http_log(('%s%s' % (url.replace(parsed.path, ''), path), 'DELETE',),
{'headers': headers}, resp, body)
+
+ store_response(resp, response_dict)
+
if resp.status < 200 or resp.status >= 300:
raise ClientException('Object DELETE failed',
http_scheme=parsed.scheme, http_host=conn.host,
@@ -1032,10 +1099,20 @@ class Connection(object):
return http_connection(self.url,
ssl_compression=self.ssl_compression)
+ def _add_response_dict(self, target_dict, kwargs):
+ if target_dict is not None:
+ response_dict = kwargs['response_dict']
+ if 'response_dicts' in target_dict:
+ target_dict['response_dicts'].append(response_dict)
+ else:
+ target_dict['response_dicts'] = [response_dict]
+ target_dict.update(response_dict)
+
def _retry(self, reset_func, func, *args, **kwargs):
self.attempts = 0
retried_auth = False
backoff = self.starting_backoff
+ caller_response_dict = kwargs.pop('response_dict', None)
while self.attempts <= self.retries:
self.attempts += 1
try:
@@ -1045,13 +1122,18 @@ class Connection(object):
if not self.http_conn:
self.http_conn = self.http_connection()
kwargs['http_conn'] = self.http_conn
+ if caller_response_dict is not None:
+ kwargs['response_dict'] = {}
rv = func(self.url, self.token, *args, **kwargs)
+ self._add_response_dict(caller_response_dict, kwargs)
return rv
except (socket.error, HTTPException):
+ self._add_response_dict(caller_response_dict, kwargs)
if self.attempts > self.retries:
raise
self.http_conn = None
except ClientException as err:
+ self._add_response_dict(caller_response_dict, kwargs)
if self.attempts > self.retries:
raise
if err.http_status == 401:
@@ -1086,9 +1168,10 @@ class Connection(object):
prefix=prefix, end_marker=end_marker,
full_listing=full_listing)
- def post_account(self, headers):
+ def post_account(self, headers, response_dict=None):
"""Wrapper for :func:`post_account`"""
- return self._retry(None, post_account, headers)
+ return self._retry(None, post_account, headers,
+ response_dict=response_dict)
def head_container(self, container):
"""Wrapper for :func:`head_container`"""
@@ -1106,32 +1189,36 @@ class Connection(object):
end_marker=end_marker, path=path,
full_listing=full_listing)
- def put_container(self, container, headers=None):
+ def put_container(self, container, headers=None, response_dict=None):
"""Wrapper for :func:`put_container`"""
- return self._retry(None, put_container, container, headers=headers)
+ return self._retry(None, put_container, container, headers=headers,
+ response_dict=response_dict)
- def post_container(self, container, headers):
+ def post_container(self, container, headers, response_dict=None):
"""Wrapper for :func:`post_container`"""
- return self._retry(None, post_container, container, headers)
+ return self._retry(None, post_container, container, headers,
+ response_dict=response_dict)
- def delete_container(self, container):
+ def delete_container(self, container, response_dict=None):
"""Wrapper for :func:`delete_container`"""
- return self._retry(None, delete_container, container)
+ return self._retry(None, delete_container, container,
+ response_dict=response_dict)
def head_object(self, container, obj):
"""Wrapper for :func:`head_object`"""
return self._retry(None, head_object, container, obj)
def get_object(self, container, obj, resp_chunk_size=None,
- query_string=None):
+ query_string=None, response_dict=None):
"""Wrapper for :func:`get_object`"""
return self._retry(None, get_object, container, obj,
resp_chunk_size=resp_chunk_size,
- query_string=query_string)
+ query_string=query_string,
+ response_dict=response_dict)
def put_object(self, container, obj, contents, content_length=None,
etag=None, chunk_size=None, content_type=None,
- headers=None, query_string=None):
+ headers=None, query_string=None, response_dict=None):
"""Wrapper for :func:`put_object`"""
def _default_reset(*args, **kwargs):
@@ -1155,13 +1242,17 @@ class Connection(object):
return self._retry(reset_func, put_object, container, obj, contents,
content_length=content_length, etag=etag,
chunk_size=chunk_size, content_type=content_type,
- headers=headers, query_string=query_string)
+ headers=headers, query_string=query_string,
+ response_dict=response_dict)
- def post_object(self, container, obj, headers):
+ def post_object(self, container, obj, headers, response_dict=None):
"""Wrapper for :func:`post_object`"""
- return self._retry(None, post_object, container, obj, headers)
+ return self._retry(None, post_object, container, obj, headers,
+ response_dict=response_dict)
- def delete_object(self, container, obj, query_string=None):
+ def delete_object(self, container, obj, query_string=None,
+ response_dict=None):
"""Wrapper for :func:`delete_object`"""
return self._retry(None, delete_object, container, obj,
- query_string=query_string)
+ query_string=query_string,
+ response_dict=response_dict)