Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[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 authored July 25, 2012
135  django/utils/datastructures.py
... ...
@@ -1,6 +1,7 @@
1 1
 import copy
2 2
 import warnings
3 3
 from types import GeneratorType
  4
+from django.utils import six
4 5
 
5 6
 
6 7
 class MergeDict(object):
@@ -31,38 +32,48 @@ def get(self, key, default=None):
31 32
         except KeyError:
32 33
             return default
33 34
 
  35
+    # This is used by MergeDicts of MultiValueDicts.
34 36
     def getlist(self, key):
35 37
         for dict_ in self.dicts:
36  
-            if key in dict_.keys():
  38
+            if key in dict_:
37 39
                 return dict_.getlist(key)
38 40
         return []
39 41
 
40  
-    def iteritems(self):
  42
+    def _iteritems(self):
41 43
         seen = set()
42 44
         for dict_ in self.dicts:
43  
-            for item in dict_.iteritems():
44  
-                k, v = item
  45
+            for item in six.iteritems(dict_):
  46
+                k = item[0]
45 47
                 if k in seen:
46 48
                     continue
47 49
                 seen.add(k)
48 50
                 yield item
49 51
 
50  
-    def iterkeys(self):
51  
-        for k, v in self.iteritems():
  52
+    def _iterkeys(self):
  53
+        for k, v in self._iteritems():
52 54
             yield k
53 55
 
54  
-    def itervalues(self):
55  
-        for k, v in self.iteritems():
  56
+    def _itervalues(self):
  57
+        for k, v in self._iteritems():
56 58
             yield v
57 59
 
58  
-    def items(self):
59  
-        return list(self.iteritems())
  60
+    if six.PY3:
  61
+        items = _iteritems
  62
+        keys = _iterkeys
  63
+        values = _itervalues
  64
+    else:
  65
+        iteritems = _iteritems
  66
+        iterkeys = _iterkeys
  67
+        itervalues = _itervalues
  68
+
  69
+        def items(self):
  70
+            return list(self.iteritems())
60 71
 
61  
-    def keys(self):
62  
-        return list(self.iterkeys())
  72
+        def keys(self):
  73
+            return list(self.iterkeys())
63 74
 
64  
-    def values(self):
65  
-        return list(self.itervalues())
  75
+        def values(self):
  76
+            return list(self.itervalues())
66 77
 
67 78
     def has_key(self, key):
68 79
         for dict_ in self.dicts:
@@ -71,7 +82,8 @@ def has_key(self, key):
71 82
         return False
72 83
 
73 84
     __contains__ = has_key
74  
-    __iter__ = iterkeys
  85
+
  86
+    __iter__ = _iterkeys
75 87
 
76 88
     def copy(self):
77 89
         """Returns a copy of this object."""
@@ -117,7 +129,7 @@ def __init__(self, data=None):
117 129
             data = list(data)
118 130
         super(SortedDict, self).__init__(data)
119 131
         if isinstance(data, dict):
120  
-            self.keyOrder = data.keys()
  132
+            self.keyOrder = list(six.iterkeys(data))
121 133
         else:
122 134
             self.keyOrder = []
123 135
             seen = set()
@@ -128,7 +140,7 @@ def __init__(self, data=None):
128 140
 
129 141
     def __deepcopy__(self, memo):
130 142
         return self.__class__([(key, copy.deepcopy(value, memo))
131  
-                               for key, value in self.iteritems()])
  143
+                               for key, value in six.iteritems(self)])
132 144
 
133 145
     def __copy__(self):
134 146
         # The Python's default copy implementation will alter the state
@@ -162,28 +174,38 @@ def popitem(self):
162 174
         self.keyOrder.remove(result[0])
163 175
         return result
164 176
 
165  
-    def items(self):
166  
-        return zip(self.keyOrder, self.values())
167  
-
168  
-    def iteritems(self):
  177
+    def _iteritems(self):
169 178
         for key in self.keyOrder:
170 179
             yield key, self[key]
171 180
 
172  
-    def keys(self):
173  
-        return self.keyOrder[:]
174  
-
175  
-    def iterkeys(self):
176  
-        return iter(self.keyOrder)
177  
-
178  
-    def values(self):
179  
-        return map(self.__getitem__, self.keyOrder)
  181
+    def _iterkeys(self):
  182
+        for key in self.keyOrder:
  183
+            yield key
180 184
 
181  
-    def itervalues(self):
  185
+    def _itervalues(self):
182 186
         for key in self.keyOrder:
183 187
             yield self[key]
184 188
 
  189
+    if six.PY3:
  190
+        items = _iteritems
  191
+        keys = _iterkeys
  192
+        values = _itervalues
  193
+    else:
  194
+        iteritems = _iteritems
  195
+        iterkeys = _iterkeys
  196
+        itervalues = _itervalues
  197
+
  198
+        def items(self):
  199
+            return list(self.iteritems())
  200
+
  201
+        def keys(self):
  202
+            return list(self.iterkeys())
  203
+
  204
+        def values(self):
  205
+            return list(self.itervalues())
  206
+
185 207
     def update(self, dict_):
186  
-        for k, v in dict_.iteritems():
  208
+        for k, v in six.iteritems(dict_):
187 209
             self[k] = v
188 210
 
189 211
     def setdefault(self, key, default):
@@ -226,7 +248,7 @@ def __repr__(self):
226 248
         Replaces the normal dict.__repr__ with a version that returns the keys
227 249
         in their sorted order.
228 250
         """
229  
-        return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
  251
+        return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in six.iteritems(self)])
230 252
 
231 253
     def clear(self):
232 254
         super(SortedDict, self).clear()
@@ -356,38 +378,41 @@ def appendlist(self, key, value):
356 378
         """Appends an item to the internal list associated with key."""
357 379
         self.setlistdefault(key).append(value)
358 380
 
359  
-    def items(self):
360  
-        """
361  
-        Returns a list of (key, value) pairs, where value is the last item in
362  
-        the list associated with the key.
363  
-        """
364  
-        return [(key, self[key]) for key in self.keys()]
365  
-
366  
-    def iteritems(self):
  381
+    def _iteritems(self):
367 382
         """
368 383
         Yields (key, value) pairs, where value is the last item in the list
369 384
         associated with the key.
370 385
         """
371  
-        for key in self.keys():
372  
-            yield (key, self[key])
373  
-
374  
-    def lists(self):
375  
-        """Returns a list of (key, list) pairs."""
376  
-        return super(MultiValueDict, self).items()
  386
+        for key in self:
  387
+            yield key, self[key]
377 388
 
378  
-    def iterlists(self):
  389
+    def _iterlists(self):
379 390
         """Yields (key, list) pairs."""
380  
-        return super(MultiValueDict, self).iteritems()
  391
+        return six.iteritems(super(MultiValueDict, self))
381 392
 
382  
-    def values(self):
383  
-        """Returns a list of the last value on every key list."""
384  
-        return [self[key] for key in self.keys()]
385  
-
386  
-    def itervalues(self):
  393
+    def _itervalues(self):
387 394
         """Yield the last value on every key list."""
388  
-        for key in self.iterkeys():
  395
+        for key in self:
389 396
             yield self[key]
390 397
 
  398
+    if six.PY3:
  399
+        items = _iteritems
  400
+        lists = _iterlists
  401
+        values = _itervalues
  402
+    else:
  403
+        iteritems = _iteritems
  404
+        iterlists = _iterlists
  405
+        itervalues = _itervalues
  406
+
  407
+        def items(self):
  408
+            return list(self.iteritems())
  409
+
  410
+        def lists(self):
  411
+            return list(self.iterlists())
  412
+
  413
+        def values(self):
  414
+            return list(self.itervalues())
  415
+
391 416
     def copy(self):
392 417
         """Returns a shallow copy of this object."""
393 418
         return copy.copy(self)
@@ -410,7 +435,7 @@ def update(self, *args, **kwargs):
410 435
                         self.setlistdefault(key).append(value)
411 436
                 except TypeError:
412 437
                     raise ValueError("MultiValueDict.update() takes either a MultiValueDict or dictionary")
413  
-        for key, value in kwargs.iteritems():
  438
+        for key, value in six.iteritems(kwargs):
414 439
             self.setlistdefault(key).append(value)
415 440
 
416 441
     def dict(self):
9  django/utils/six.py
@@ -355,4 +355,13 @@ def with_metaclass(meta, base=object):
355 355
 
356 356
 ### Additional customizations for Django ###
357 357
 
  358
+if PY3:
  359
+    _iterlists = "lists"
  360
+else:
  361
+    _iterlists = "iterlists"
  362
+
  363
+def iterlists(d):
  364
+    """Return an iterator over the values of a MultiValueDict."""
  365
+    return getattr(d, _iterlists)()
  366
+
358 367
 add_move(MovedModule("_dummy_thread", "dummy_thread"))
15  docs/topics/python3.txt
@@ -120,3 +120,18 @@ If you need different code in Python 2 and Python 3, check :data:`six.PY3`::
120 120
 
121 121
 This is a last resort solution when :mod:`six` doesn't provide an appropriate
122 122
 function.
  123
+
  124
+.. module:: django.utils.six
  125
+
  126
+Customizations of six
  127
+=====================
  128
+
  129
+The version of six bundled with Django includes a few additional tools:
  130
+
  131
+.. function:: iterlists(MultiValueDict)
  132
+
  133
+    Returns an iterator over the lists of values of a
  134
+    :class:`~django.utils.datastructures.MultiValueDict`. This replaces
  135
+    :meth:`~django.utils.datastructures.MultiValueDict.iterlists()` on Python
  136
+    2 and :meth:`~django.utils.datastructures.MultiValueDict.lists()` on
  137
+    Python 3.
68  tests/regressiontests/utils/datastructures.py
@@ -9,6 +9,7 @@
9 9
 from django.test import SimpleTestCase
10 10
 from django.utils.datastructures import (DictWrapper, ImmutableList,
11 11
     MultiValueDict, MultiValueDictKeyError, MergeDict, SortedDict)
  12
+from django.utils import six
12 13
 
13 14
 
14 15
 class SortedDictTests(SimpleTestCase):
@@ -25,19 +26,19 @@ def setUp(self):
25 26
         self.d2[7] = 'seven'
26 27
 
27 28
     def test_basic_methods(self):
28  
-        self.assertEqual(self.d1.keys(), [7, 1, 9])
29  
-        self.assertEqual(self.d1.values(), ['seven', 'one', 'nine'])
30  
-        self.assertEqual(self.d1.items(), [(7, 'seven'), (1, 'one'), (9, 'nine')])
  29
+        self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9])
  30
+        self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'one', 'nine'])
  31
+        self.assertEqual(list(six.iteritems(self.d1)), [(7, 'seven'), (1, 'one'), (9, 'nine')])
31 32
 
32 33
     def test_overwrite_ordering(self):
33  
-        """ Overwriting an item keeps it's place. """
  34
+        """ Overwriting an item keeps its place. """
34 35
         self.d1[1] = 'ONE'
35  
-        self.assertEqual(self.d1.values(), ['seven', 'ONE', 'nine'])
  36
+        self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'ONE', 'nine'])
36 37
 
37 38
     def test_append_items(self):
38 39
         """ New items go to the end. """
39 40
         self.d1[0] = 'nil'
40  
-        self.assertEqual(self.d1.keys(), [7, 1, 9, 0])
  41
+        self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9, 0])
41 42
 
42 43
     def test_delete_and_insert(self):
43 44
         """
@@ -45,18 +46,22 @@ def test_delete_and_insert(self):
45 46
         at the end.
46 47
         """
47 48
         del self.d2[7]
48  
-        self.assertEqual(self.d2.keys(), [1, 9, 0])
  49
+        self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0])
49 50
         self.d2[7] = 'lucky number 7'
50  
-        self.assertEqual(self.d2.keys(), [1, 9, 0, 7])
  51
+        self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0, 7])
51 52
 
52  
-    def test_change_keys(self):
53  
-        """
54  
-        Changing the keys won't do anything, it's only a copy of the
55  
-        keys dict.
56  
-        """
57  
-        k = self.d2.keys()
58  
-        k.remove(9)
59  
-        self.assertEqual(self.d2.keys(), [1, 9, 0, 7])
  53
+    if not six.PY3:
  54
+        def test_change_keys(self):
  55
+            """
  56
+            Changing the keys won't do anything, it's only a copy of the
  57
+            keys dict.
  58
+
  59
+            This test doesn't make sense under Python 3 because keys is
  60
+            an iterator.
  61
+            """
  62
+            k = self.d2.keys()
  63
+            k.remove(9)
  64
+            self.assertEqual(self.d2.keys(), [1, 9, 0, 7])
60 65
 
61 66
     def test_init_keys(self):
62 67
         """
@@ -68,18 +73,18 @@ def test_init_keys(self):
68 73
         tuples = ((2, 'two'), (1, 'one'), (2, 'second-two'))
69 74
         d = SortedDict(tuples)
70 75
 
71  
-        self.assertEqual(d.keys(), [2, 1])
  76
+        self.assertEqual(list(six.iterkeys(d)), [2, 1])
72 77
 
73 78
         real_dict = dict(tuples)
74  
-        self.assertEqual(sorted(real_dict.values()), ['one', 'second-two'])
  79
+        self.assertEqual(sorted(six.itervalues(real_dict)), ['one', 'second-two'])
75 80
 
76 81
         # Here the order of SortedDict values *is* what we are testing
77  
-        self.assertEqual(d.values(), ['second-two', 'one'])
  82
+        self.assertEqual(list(six.itervalues(d)), ['second-two', 'one'])
78 83
 
79 84
     def test_overwrite(self):
80 85
         self.d1[1] = 'not one'
81 86
         self.assertEqual(self.d1[1], 'not one')
82  
-        self.assertEqual(self.d1.keys(), self.d1.copy().keys())
  87
+        self.assertEqual(list(six.iterkeys(self.d1)), list(six.iterkeys(self.d1.copy())))
83 88
 
84 89
     def test_append(self):
85 90
         self.d1[13] = 'thirteen'
@@ -115,8 +120,8 @@ def test_pickle(self):
115 120
     def test_copy(self):
116 121
         orig = SortedDict(((1, "one"), (0, "zero"), (2, "two")))
117 122
         copied = copy.copy(orig)
118  
-        self.assertEqual(orig.keys(), [1, 0, 2])
119  
-        self.assertEqual(copied.keys(), [1, 0, 2])
  123
+        self.assertEqual(list(six.iterkeys(orig)), [1, 0, 2])
  124
+        self.assertEqual(list(six.iterkeys(copied)), [1, 0, 2])
120 125
 
121 126
     def test_clear(self):
122 127
         self.d1.clear()
@@ -178,12 +183,12 @@ def test_mergedict_merges_multivaluedict(self):
178 183
         self.assertEqual(mm.getlist('key4'), ['value5', 'value6'])
179 184
         self.assertEqual(mm.getlist('undefined'), [])
180 185
 
181  
-        self.assertEqual(sorted(mm.keys()), ['key1', 'key2', 'key4'])
182  
-        self.assertEqual(len(mm.values()), 3)
  186
+        self.assertEqual(sorted(six.iterkeys(mm)), ['key1', 'key2', 'key4'])
  187
+        self.assertEqual(len(list(six.itervalues(mm))), 3)
183 188
 
184  
-        self.assertTrue('value1' in mm.values())
  189
+        self.assertTrue('value1' in six.itervalues(mm))
185 190
 
186  
-        self.assertEqual(sorted(mm.items(), key=lambda k: k[0]),
  191
+        self.assertEqual(sorted(six.iteritems(mm), key=lambda k: k[0]),
187 192
                           [('key1', 'value1'), ('key2', 'value3'),
188 193
                            ('key4', 'value6')])
189 194
 
@@ -201,10 +206,10 @@ def test_multivaluedict(self):
201 206
         self.assertEqual(d['name'], 'Simon')
202 207
         self.assertEqual(d.get('name'), 'Simon')
203 208
         self.assertEqual(d.getlist('name'), ['Adrian', 'Simon'])
204  
-        self.assertEqual(list(d.iteritems()),
  209
+        self.assertEqual(list(six.iteritems(d)),
205 210
                           [('position', 'Developer'), ('name', 'Simon')])
206 211
 
207  
-        self.assertEqual(list(d.iterlists()),
  212
+        self.assertEqual(list(six.iterlists(d)),
208 213
                           [('position', ['Developer']),
209 214
                            ('name', ['Adrian', 'Simon'])])
210 215
 
@@ -224,8 +229,7 @@ def test_multivaluedict(self):
224 229
 
225 230
         d.setlist('lastname', ['Holovaty', 'Willison'])
226 231
         self.assertEqual(d.getlist('lastname'), ['Holovaty', 'Willison'])
227  
-        self.assertEqual(d.values(), ['Developer', 'Simon', 'Willison'])
228  
-        self.assertEqual(list(d.itervalues()),
  232
+        self.assertEqual(list(six.itervalues(d)),
229 233
                           ['Developer', 'Simon', 'Willison'])
230 234
 
231 235
     def test_appendlist(self):
@@ -260,8 +264,8 @@ def test_dict_translation(self):
260 264
             'pm': ['Rory'],
261 265
         })
262 266
         d = mvd.dict()
263  
-        self.assertEqual(d.keys(), mvd.keys())
264  
-        for key in mvd.keys():
  267
+        self.assertEqual(list(six.iterkeys(d)), list(six.iterkeys(mvd)))
  268
+        for key in six.iterkeys(mvd):
265 269
             self.assertEqual(d[key], mvd[key])
266 270
 
267 271
         self.assertEqual({}, MultiValueDict().dict())

0 notes on commit ab6cd1c

Please sign in to comment.
Something went wrong with that request. Please try again.