Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[py3] Updated dict-like data structures for Python 3.

The keys/items/values methods return iterators in Python 3, and the
iterkeys/items/values methods don't exist in Python 3. The behavior
under Python 2 is unchanged.
  • Loading branch information...
commit ab6cd1c839b136cbc94178da433b2e97ab7f6061 1 parent 4b5cb11
Aymeric Augustin aaugustin authored
135 django/utils/datastructures.py
View
@@ -1,6 +1,7 @@
import copy
import warnings
from types import GeneratorType
+from django.utils import six
class MergeDict(object):
@@ -31,38 +32,48 @@ def get(self, key, default=None):
except KeyError:
return default
+ # This is used by MergeDicts of MultiValueDicts.
def getlist(self, key):
for dict_ in self.dicts:
- if key in dict_.keys():
+ if key in dict_:
return dict_.getlist(key)
return []
- def iteritems(self):
+ def _iteritems(self):
seen = set()
for dict_ in self.dicts:
- for item in dict_.iteritems():
- k, v = item
+ for item in six.iteritems(dict_):
+ k = item[0]
if k in seen:
continue
seen.add(k)
yield item
- def iterkeys(self):
- for k, v in self.iteritems():
+ def _iterkeys(self):
+ for k, v in self._iteritems():
yield k
- def itervalues(self):
- for k, v in self.iteritems():
+ def _itervalues(self):
+ for k, v in self._iteritems():
yield v
- def items(self):
- return list(self.iteritems())
+ if six.PY3:
+ items = _iteritems
+ keys = _iterkeys
+ values = _itervalues
+ else:
+ iteritems = _iteritems
+ iterkeys = _iterkeys
+ itervalues = _itervalues
+
+ def items(self):
+ return list(self.iteritems())
- def keys(self):
- return list(self.iterkeys())
+ def keys(self):
+ return list(self.iterkeys())
- def values(self):
- return list(self.itervalues())
+ def values(self):
+ return list(self.itervalues())
def has_key(self, key):
for dict_ in self.dicts:
@@ -71,7 +82,8 @@ def has_key(self, key):
return False
__contains__ = has_key
- __iter__ = iterkeys
+
+ __iter__ = _iterkeys
def copy(self):
"""Returns a copy of this object."""
@@ -117,7 +129,7 @@ def __init__(self, data=None):
data = list(data)
super(SortedDict, self).__init__(data)
if isinstance(data, dict):
- self.keyOrder = data.keys()
+ self.keyOrder = list(six.iterkeys(data))
else:
self.keyOrder = []
seen = set()
@@ -128,7 +140,7 @@ def __init__(self, data=None):
def __deepcopy__(self, memo):
return self.__class__([(key, copy.deepcopy(value, memo))
- for key, value in self.iteritems()])
+ for key, value in six.iteritems(self)])
def __copy__(self):
# The Python's default copy implementation will alter the state
@@ -162,28 +174,38 @@ def popitem(self):
self.keyOrder.remove(result[0])
return result
- def items(self):
- return zip(self.keyOrder, self.values())
-
- def iteritems(self):
+ def _iteritems(self):
for key in self.keyOrder:
yield key, self[key]
- def keys(self):
- return self.keyOrder[:]
-
- def iterkeys(self):
- return iter(self.keyOrder)
-
- def values(self):
- return map(self.__getitem__, self.keyOrder)
+ def _iterkeys(self):
+ for key in self.keyOrder:
+ yield key
- def itervalues(self):
+ def _itervalues(self):
for key in self.keyOrder:
yield self[key]
+ if six.PY3:
+ items = _iteritems
+ keys = _iterkeys
+ values = _itervalues
+ else:
+ iteritems = _iteritems
+ iterkeys = _iterkeys
+ itervalues = _itervalues
+
+ def items(self):
+ return list(self.iteritems())
+
+ def keys(self):
+ return list(self.iterkeys())
+
+ def values(self):
+ return list(self.itervalues())
+
def update(self, dict_):
- for k, v in dict_.iteritems():
+ for k, v in six.iteritems(dict_):
self[k] = v
def setdefault(self, key, default):
@@ -226,7 +248,7 @@ def __repr__(self):
Replaces the normal dict.__repr__ with a version that returns the keys
in their sorted order.
"""
- return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
+ return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in six.iteritems(self)])
def clear(self):
super(SortedDict, self).clear()
@@ -356,38 +378,41 @@ def appendlist(self, key, value):
"""Appends an item to the internal list associated with key."""
self.setlistdefault(key).append(value)
- def items(self):
- """
- Returns a list of (key, value) pairs, where value is the last item in
- the list associated with the key.
- """
- return [(key, self[key]) for key in self.keys()]
-
- def iteritems(self):
+ def _iteritems(self):
"""
Yields (key, value) pairs, where value is the last item in the list
associated with the key.
"""
- for key in self.keys():
- yield (key, self[key])
-
- def lists(self):
- """Returns a list of (key, list) pairs."""
- return super(MultiValueDict, self).items()
+ for key in self:
+ yield key, self[key]
- def iterlists(self):
+ def _iterlists(self):
"""Yields (key, list) pairs."""
- return super(MultiValueDict, self).iteritems()
+ return six.iteritems(super(MultiValueDict, self))
- def values(self):
- """Returns a list of the last value on every key list."""
- return [self[key] for key in self.keys()]
-
- def itervalues(self):
+ def _itervalues(self):
"""Yield the last value on every key list."""
- for key in self.iterkeys():
+ for key in self:
yield self[key]
+ if six.PY3:
+ items = _iteritems
+ lists = _iterlists
+ values = _itervalues
+ else:
+ iteritems = _iteritems
+ iterlists = _iterlists
+ itervalues = _itervalues
+
+ def items(self):
+ return list(self.iteritems())
+
+ def lists(self):
+ return list(self.iterlists())
+
+ def values(self):
+ return list(self.itervalues())
+
def copy(self):
"""Returns a shallow copy of this object."""
return copy.copy(self)
@@ -410,7 +435,7 @@ def update(self, *args, **kwargs):
self.setlistdefault(key).append(value)
except TypeError:
raise ValueError("MultiValueDict.update() takes either a MultiValueDict or dictionary")
- for key, value in kwargs.iteritems():
+ for key, value in six.iteritems(kwargs):
self.setlistdefault(key).append(value)
def dict(self):
9 django/utils/six.py
View
@@ -355,4 +355,13 @@ def with_metaclass(meta, base=object):
### Additional customizations for Django ###
+if PY3:
+ _iterlists = "lists"
+else:
+ _iterlists = "iterlists"
+
+def iterlists(d):
+ """Return an iterator over the values of a MultiValueDict."""
+ return getattr(d, _iterlists)()
+
add_move(MovedModule("_dummy_thread", "dummy_thread"))
15 docs/topics/python3.txt
View
@@ -120,3 +120,18 @@ If you need different code in Python 2 and Python 3, check :data:`six.PY3`::
This is a last resort solution when :mod:`six` doesn't provide an appropriate
function.
+
+.. module:: django.utils.six
+
+Customizations of six
+=====================
+
+The version of six bundled with Django includes a few additional tools:
+
+.. function:: iterlists(MultiValueDict)
+
+ Returns an iterator over the lists of values of a
+ :class:`~django.utils.datastructures.MultiValueDict`. This replaces
+ :meth:`~django.utils.datastructures.MultiValueDict.iterlists()` on Python
+ 2 and :meth:`~django.utils.datastructures.MultiValueDict.lists()` on
+ Python 3.
68 tests/regressiontests/utils/datastructures.py
View
@@ -9,6 +9,7 @@
from django.test import SimpleTestCase
from django.utils.datastructures import (DictWrapper, ImmutableList,
MultiValueDict, MultiValueDictKeyError, MergeDict, SortedDict)
+from django.utils import six
class SortedDictTests(SimpleTestCase):
@@ -25,19 +26,19 @@ def setUp(self):
self.d2[7] = 'seven'
def test_basic_methods(self):
- self.assertEqual(self.d1.keys(), [7, 1, 9])
- self.assertEqual(self.d1.values(), ['seven', 'one', 'nine'])
- self.assertEqual(self.d1.items(), [(7, 'seven'), (1, 'one'), (9, 'nine')])
+ self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9])
+ self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'one', 'nine'])
+ self.assertEqual(list(six.iteritems(self.d1)), [(7, 'seven'), (1, 'one'), (9, 'nine')])
def test_overwrite_ordering(self):
- """ Overwriting an item keeps it's place. """
+ """ Overwriting an item keeps its place. """
self.d1[1] = 'ONE'
- self.assertEqual(self.d1.values(), ['seven', 'ONE', 'nine'])
+ self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'ONE', 'nine'])
def test_append_items(self):
""" New items go to the end. """
self.d1[0] = 'nil'
- self.assertEqual(self.d1.keys(), [7, 1, 9, 0])
+ self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9, 0])
def test_delete_and_insert(self):
"""
@@ -45,18 +46,22 @@ def test_delete_and_insert(self):
at the end.
"""
del self.d2[7]
- self.assertEqual(self.d2.keys(), [1, 9, 0])
+ self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0])
self.d2[7] = 'lucky number 7'
- self.assertEqual(self.d2.keys(), [1, 9, 0, 7])
+ self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0, 7])
- def test_change_keys(self):
- """
- Changing the keys won't do anything, it's only a copy of the
- keys dict.
- """
- k = self.d2.keys()
- k.remove(9)
- self.assertEqual(self.d2.keys(), [1, 9, 0, 7])
+ if not six.PY3:
+ def test_change_keys(self):
+ """
+ Changing the keys won't do anything, it's only a copy of the
+ keys dict.
+
+ This test doesn't make sense under Python 3 because keys is
+ an iterator.
+ """
+ k = self.d2.keys()
+ k.remove(9)
+ self.assertEqual(self.d2.keys(), [1, 9, 0, 7])
def test_init_keys(self):
"""
@@ -68,18 +73,18 @@ def test_init_keys(self):
tuples = ((2, 'two'), (1, 'one'), (2, 'second-two'))
d = SortedDict(tuples)
- self.assertEqual(d.keys(), [2, 1])
+ self.assertEqual(list(six.iterkeys(d)), [2, 1])
real_dict = dict(tuples)
- self.assertEqual(sorted(real_dict.values()), ['one', 'second-two'])
+ self.assertEqual(sorted(six.itervalues(real_dict)), ['one', 'second-two'])
# Here the order of SortedDict values *is* what we are testing
- self.assertEqual(d.values(), ['second-two', 'one'])
+ self.assertEqual(list(six.itervalues(d)), ['second-two', 'one'])
def test_overwrite(self):
self.d1[1] = 'not one'
self.assertEqual(self.d1[1], 'not one')
- self.assertEqual(self.d1.keys(), self.d1.copy().keys())
+ self.assertEqual(list(six.iterkeys(self.d1)), list(six.iterkeys(self.d1.copy())))
def test_append(self):
self.d1[13] = 'thirteen'
@@ -115,8 +120,8 @@ def test_pickle(self):
def test_copy(self):
orig = SortedDict(((1, "one"), (0, "zero"), (2, "two")))
copied = copy.copy(orig)
- self.assertEqual(orig.keys(), [1, 0, 2])
- self.assertEqual(copied.keys(), [1, 0, 2])
+ self.assertEqual(list(six.iterkeys(orig)), [1, 0, 2])
+ self.assertEqual(list(six.iterkeys(copied)), [1, 0, 2])
def test_clear(self):
self.d1.clear()
@@ -178,12 +183,12 @@ def test_mergedict_merges_multivaluedict(self):
self.assertEqual(mm.getlist('key4'), ['value5', 'value6'])
self.assertEqual(mm.getlist('undefined'), [])
- self.assertEqual(sorted(mm.keys()), ['key1', 'key2', 'key4'])
- self.assertEqual(len(mm.values()), 3)
+ self.assertEqual(sorted(six.iterkeys(mm)), ['key1', 'key2', 'key4'])
+ self.assertEqual(len(list(six.itervalues(mm))), 3)
- self.assertTrue('value1' in mm.values())
+ self.assertTrue('value1' in six.itervalues(mm))
- self.assertEqual(sorted(mm.items(), key=lambda k: k[0]),
+ self.assertEqual(sorted(six.iteritems(mm), key=lambda k: k[0]),
[('key1', 'value1'), ('key2', 'value3'),
('key4', 'value6')])
@@ -201,10 +206,10 @@ def test_multivaluedict(self):
self.assertEqual(d['name'], 'Simon')
self.assertEqual(d.get('name'), 'Simon')
self.assertEqual(d.getlist('name'), ['Adrian', 'Simon'])
- self.assertEqual(list(d.iteritems()),
+ self.assertEqual(list(six.iteritems(d)),
[('position', 'Developer'), ('name', 'Simon')])
- self.assertEqual(list(d.iterlists()),
+ self.assertEqual(list(six.iterlists(d)),
[('position', ['Developer']),
('name', ['Adrian', 'Simon'])])
@@ -224,8 +229,7 @@ def test_multivaluedict(self):
d.setlist('lastname', ['Holovaty', 'Willison'])
self.assertEqual(d.getlist('lastname'), ['Holovaty', 'Willison'])
- self.assertEqual(d.values(), ['Developer', 'Simon', 'Willison'])
- self.assertEqual(list(d.itervalues()),
+ self.assertEqual(list(six.itervalues(d)),
['Developer', 'Simon', 'Willison'])
def test_appendlist(self):
@@ -260,8 +264,8 @@ def test_dict_translation(self):
'pm': ['Rory'],
})
d = mvd.dict()
- self.assertEqual(d.keys(), mvd.keys())
- for key in mvd.keys():
+ self.assertEqual(list(six.iterkeys(d)), list(six.iterkeys(mvd)))
+ for key in six.iterkeys(mvd):
self.assertEqual(d[key], mvd[key])
self.assertEqual({}, MultiValueDict().dict())
Please sign in to comment.
Something went wrong with that request. Please try again.