From d9cdeaa58e1cd5628941e8bded9910feda86d2f3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 21 Apr 2015 18:11:00 +0200 Subject: Port multidict to Python 3 --- paste/util/multidict.py | 39 ++++++++++++++++++---- tests/test_multidict.py | 86 +++++++++++++++++++++++++------------------------ 2 files changed, 76 insertions(+), 49 deletions(-) diff --git a/paste/util/multidict.py b/paste/util/multidict.py index eb32e53..701d1ac 100644 --- a/paste/util/multidict.py +++ b/paste/util/multidict.py @@ -61,7 +61,7 @@ class MultiDict(DictMixin): """ result = [] for k, v in self._items: - if key == k: + if type(key) == type(k) and key == k: result.append(v) return result @@ -117,7 +117,7 @@ class MultiDict(DictMixin): items = self._items found = False for i in range(len(items)-1, -1, -1): - if items[i][0] == key: + if type(items[i][0]) == type(key) and items[i][0] == key: del items[i] found = True if not found: @@ -125,7 +125,7 @@ class MultiDict(DictMixin): def __contains__(self, key): for k, v in self._items: - if k == key: + if type(k) == type(key) and k == key: return True return False @@ -149,7 +149,7 @@ class MultiDict(DictMixin): raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) for i in range(len(self._items)): - if self._items[i][0] == key: + if type(self._items[i][0]) == type(key) and self._items[i][0] == key: v = self._items[i][1] del self._items[i] return v @@ -233,6 +233,20 @@ class UnicodeMultiDict(DictMixin): self.encoding = encoding self.errors = errors self.decode_keys = decode_keys + if self.decode_keys: + items = self.multi._items + for index, item in enumerate(items): + key, value = item + key = self._encode_key(key) + items[index] = (key, value) + + def _encode_key(self, key): + if self.decode_keys: + try: + key = key.encode(self.encoding, self.errors) + except AttributeError: + pass + return key def _decode_key(self, key): if self.decode_keys: @@ -252,9 +266,10 @@ class UnicodeMultiDict(DictMixin): if isinstance(value, cgi.FieldStorage): # decode FieldStorage's field name and filename value = copy.copy(value) - if self.decode_keys: + if self.decode_keys and isinstance(value.name, six.binary_type): value.name = value.name.decode(self.encoding, self.errors) - value.filename = value.filename.decode(self.encoding, self.errors) + if six.PY2: + value.filename = value.filename.decode(self.encoding, self.errors) else: try: value = value.decode(self.encoding, self.errors) @@ -263,21 +278,25 @@ class UnicodeMultiDict(DictMixin): return value def __getitem__(self, key): + key = self._encode_key(key) return self._decode_value(self.multi.__getitem__(key)) def __setitem__(self, key, value): + key = self._encode_key(key) self.multi.__setitem__(key, value) def add(self, key, value): """ Add the key and value, not overwriting any previous value. """ + key = self._encode_key(key) self.multi.add(key, value) def getall(self, key): """ Return a list of all values matching the key (may be an empty list) """ + key = self._encode_key(key) return [self._decode_value(v) for v in self.multi.getall(key)] def getone(self, key): @@ -285,6 +304,7 @@ class UnicodeMultiDict(DictMixin): Get one value matching the key, raising a KeyError if multiple values were found. """ + key = self._encode_key(key) return self._decode_value(self.multi.getone(key)) def mixed(self): @@ -316,9 +336,11 @@ class UnicodeMultiDict(DictMixin): return unicode_dict def __delitem__(self, key): + key = self._encode_key(key) self.multi.__delitem__(key) def __contains__(self, key): + key = self._encode_key(key) return self.multi.__contains__(key) has_key = __contains__ @@ -327,12 +349,15 @@ class UnicodeMultiDict(DictMixin): self.multi.clear() def copy(self): - return UnicodeMultiDict(self.multi.copy(), self.encoding, self.errors) + return UnicodeMultiDict(self.multi.copy(), self.encoding, self.errors, + decode_keys=self.decode_keys) def setdefault(self, key, default=None): + key = self._encode_key(key) return self._decode_value(self.multi.setdefault(key, default)) def pop(self, key, *args): + key = self._encode_key(key) return self._decode_value(self.multi.pop(key, *args)) def popitem(self): diff --git a/tests/test_multidict.py b/tests/test_multidict.py index 820331e..ea5186a 100644 --- a/tests/test_multidict.py +++ b/tests/test_multidict.py @@ -59,15 +59,17 @@ def test_unicode_dict(): _test_unicode_dict(decode_param_names=True) def _test_unicode_dict(decode_param_names=False): - d = UnicodeMultiDict(MultiDict({'a': 'a test'})) + d = UnicodeMultiDict(MultiDict({b'a': 'a test'})) d.encoding = 'utf-8' d.errors = 'ignore' if decode_param_names: key_str = six.text_type + k = lambda key: key d.decode_keys = True else: - key_str = str + key_str = six.binary_type + k = lambda key: key.encode() def assert_unicode(obj): assert isinstance(obj, six.text_type) @@ -80,67 +82,67 @@ def _test_unicode_dict(decode_param_names=False): assert isinstance(key, key_str) assert isinstance(value, six.text_type) - assert d.items() == [('a', u'a test')] + assert d.items() == [(k('a'), u'a test')] map(assert_key_str, d.keys()) map(assert_unicode, d.values()) - d['b'] = '2 test' - d['c'] = '3 test' - assert d.items() == [('a', u'a test'), ('b', u'2 test'), ('c', u'3 test')] - map(assert_unicode_item, d.items()) + d[b'b'] = b'2 test' + d[b'c'] = b'3 test' + assert d.items() == [(k('a'), u'a test'), (k('b'), u'2 test'), (k('c'), u'3 test')] + list(map(assert_unicode_item, d.items())) - d['b'] = '4 test' - assert d.items() == [('a', u'a test'), ('c', u'3 test'), ('b', u'4 test')] - map(assert_unicode_item, d.items()) + d[k('b')] = b'4 test' + assert d.items() == [(k('a'), u'a test'), (k('c'), u'3 test'), (k('b'), u'4 test')], d.items() + list(map(assert_unicode_item, d.items())) - d.add('b', '5 test') - assert_raises(KeyError, d.getone, "b") - assert d.getall('b') == [u'4 test', u'5 test'] + d.add(k('b'), b'5 test') + assert_raises(KeyError, d.getone, k("b")) + assert d.getall(k('b')) == [u'4 test', u'5 test'] map(assert_unicode, d.getall('b')) - assert d.items() == [('a', u'a test'), ('c', u'3 test'), ('b', u'4 test'), - ('b', u'5 test')] - map(assert_unicode_item, d.items()) + assert d.items() == [(k('a'), u'a test'), (k('c'), u'3 test'), (k('b'), u'4 test'), + (k('b'), u'5 test')] + list(map(assert_unicode_item, d.items())) - del d['b'] - assert d.items() == [('a', u'a test'), ('c', u'3 test')] - map(assert_unicode_item, d.items()) + del d[k('b')] + assert d.items() == [(k('a'), u'a test'), (k('c'), u'3 test')] + list(map(assert_unicode_item, d.items())) assert d.pop('xxx', u'5 test') == u'5 test' assert isinstance(d.pop('xxx', u'5 test'), six.text_type) - assert d.getone('a') == u'a test' - assert isinstance(d.getone('a'), six.text_type) - assert d.popitem() == ('c', u'3 test') - d['c'] = '3 test' + assert d.getone(k('a')) == u'a test' + assert isinstance(d.getone(k('a')), six.text_type) + assert d.popitem() == (k('c'), u'3 test') + d[k('c')] = b'3 test' assert_unicode_item(d.popitem()) - assert d.items() == [('a', u'a test')] - map(assert_unicode_item, d.items()) + assert d.items() == [(k('a'), u'a test')] + list(map(assert_unicode_item, d.items())) item = [] - assert d.setdefault('z', item) is item + assert d.setdefault(k('z'), item) is item items = d.items() - assert items == [('a', u'a test'), ('z', item)] + assert items == [(k('a'), u'a test'), (k('z'), item)] assert isinstance(items[1][0], key_str) assert isinstance(items[1][1], list) - assert isinstance(d.setdefault('y', 'y test'), six.text_type) - assert isinstance(d['y'], six.text_type) + assert isinstance(d.setdefault(k('y'), b'y test'), six.text_type) + assert isinstance(d[k('y')], six.text_type) - assert d.mixed() == {u'a': u'a test', u'y': u'y test', u'z': item} - assert d.dict_of_lists() == {u'a': [u'a test'], u'y': [u'y test'], - u'z': [item]} - del d['z'] - map(assert_unicode_item, six.iteritems(d.mixed())) - map(assert_unicode_item, [(k, v[0]) for \ - k, v in six.iteritems(d.dict_of_lists())]) + assert d.mixed() == {k('a'): u'a test', k('y'): u'y test', k('z'): item} + assert d.dict_of_lists() == {k('a'): [u'a test'], k('y'): [u'y test'], + k('z'): [item]} + del d[k('z')] + list(map(assert_unicode_item, six.iteritems(d.mixed()))) + list(map(assert_unicode_item, [(key, value[0]) for \ + key, value in six.iteritems(d.dict_of_lists())])) - assert u'a' in d + assert k('a') in d dcopy = d.copy() assert dcopy is not d assert dcopy == d - d['x'] = 'x test' + d[k('x')] = 'x test' assert dcopy != d d[(1, None)] = (None, 1) - assert d.items() == [('a', u'a test'), ('y', u'y test'), ('x', u'x test'), + assert d.items() == [(k('a'), u'a test'), (k('y'), u'y test'), (k('x'), u'x test'), ((1, None), (None, 1))] item = d.items()[-1] assert isinstance(item[0], tuple) @@ -150,12 +152,12 @@ def _test_unicode_dict(decode_param_names=False): fs.name = 'thefile' fs.filename = 'hello.txt' fs.file = StringIO('hello') - d['f'] = fs - ufs = d['f'] + d[k('f')] = fs + ufs = d[k('f')] assert isinstance(ufs, cgi.FieldStorage) assert ufs is not fs assert ufs.name == fs.name - assert isinstance(ufs.name, key_str) + assert isinstance(ufs.name, str if six.PY3 else key_str) assert ufs.filename == fs.filename assert isinstance(ufs.filename, six.text_type) assert isinstance(ufs.value, str) -- cgit v1.2.1