From a1e2bcde4a54a33054c7dcb31c3c3b0e6d72d021 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Tue, 23 Aug 2016 16:17:21 -0700 Subject: [PATCH] Accept more types of input for headers/meta Previously, we only accepted iterables of strings like 'Header: Value'. Now, we'll also accept lists of tuples like ('Header', 'Value') as well as dictionaries like {'Header': 'Value'}. This should be more intuitive for application developers, who are already used to being able to pass dicts or lists of tuples to libraries like requests. Change-Id: I93ed2f1e8305f0168b7a4bd90c205b04730da836 --- swiftclient/service.py | 8 +++++--- swiftclient/utils.py | 22 ++++++++++++++++------ tests/unit/test_service.py | 19 ++++++++++++++++--- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/swiftclient/service.py b/swiftclient/service.py index de30d502..8c6880ae 100644 --- a/swiftclient/service.py +++ b/swiftclient/service.py @@ -29,11 +29,10 @@ from random import shuffle from time import time from threading import Thread -from six import StringIO, text_type +from six import Iterator, StringIO, string_types, text_type from six.moves.queue import Queue from six.moves.queue import Empty as QueueEmpty from six.moves.urllib.parse import quote -from six import Iterator, string_types import json @@ -274,7 +273,10 @@ def split_headers(options, prefix=''): """ Splits 'Key: Value' strings and returns them as a dictionary. - :param options: An array of 'Key: Value' strings + :param options: Must be one of: + * an iterable of 'Key: Value' strings + * an iterable of ('Key', 'Value') pairs + * a dict of {'Key': 'Value'} pairs :param prefix: String to prepend to all of the keys in the dictionary. reporting. """ diff --git a/swiftclient/utils.py b/swiftclient/utils.py index e14602dc..aa8b28a5 100644 --- a/swiftclient/utils.py +++ b/swiftclient/utils.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Miscellaneous utility functions for use with Swift.""" +import collections import gzip import hashlib import hmac @@ -148,15 +149,24 @@ def parse_api_response(headers, body): def split_request_headers(options, prefix=''): headers = {} + if isinstance(options, collections.Mapping): + options = options.items() for item in options: - split_item = item.split(':', 1) - if len(split_item) == 2: - headers[(prefix + split_item[0]).title()] = split_item[1].strip() - else: + if isinstance(item, six.string_types): + if ':' not in item: + raise ValueError( + "Metadata parameter %s must contain a ':'.\n" + "Example: 'Color:Blue' or 'Size:Large'" + % item + ) + item = item.split(':', 1) + if len(item) != 2: raise ValueError( - "Metadata parameter %s must contain a ':'.\n%s" - % (item, "Example: 'Color:Blue' or 'Size:Large'") + "Metadata parameter %r must have exactly two items.\n" + "Example: ('Color', 'Blue') or ['Size', 'Large']" + % (item, ) ) + headers[(prefix + item[0]).title()] = item[1].strip() return headers diff --git a/tests/unit/test_service.py b/tests/unit/test_service.py index e8385b7d..2fc827ca 100644 --- a/tests/unit/test_service.py +++ b/tests/unit/test_service.py @@ -592,11 +592,24 @@ def test_split_headers_prefix(self): actual = swiftclient.service.split_headers(mock_headers, 'prefix-') self.assertEqual(expected, actual) - def test_split_headers_error(self): - mock_headers = ['notvalid'] + def test_split_headers_list_of_tuples(self): + mock_headers = [('color', 'blue'), ('size', 'large')] + expected = {'Prefix-Color': 'blue', 'Prefix-Size': 'large'} + + actual = swiftclient.service.split_headers(mock_headers, 'prefix-') + self.assertEqual(expected, actual) + def test_split_headers_dict(self): + expected = {'Color': 'blue', 'Size': 'large'} + + actual = swiftclient.service.split_headers(expected) + self.assertEqual(expected, actual) + + def test_split_headers_error(self): + self.assertRaises(SwiftError, swiftclient.service.split_headers, + ['notvalid']) self.assertRaises(SwiftError, swiftclient.service.split_headers, - mock_headers) + [('also', 'not', 'valid')]) class TestSwiftUploadObject(unittest.TestCase):