Removed django.utils.datastructures.SortedDict per deprecation timeline.
This commit is contained in:
parent
41f0d3d3bc
commit
c820892eed
|
@ -1,132 +1,7 @@
|
||||||
import copy
|
import copy
|
||||||
import warnings
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.deprecation import RemovedInDjango19Warning
|
|
||||||
|
|
||||||
|
|
||||||
class SortedDict(dict):
|
|
||||||
"""
|
|
||||||
A dictionary that keeps its keys in the order in which they're inserted.
|
|
||||||
"""
|
|
||||||
def __new__(cls, *args, **kwargs):
|
|
||||||
instance = super(SortedDict, cls).__new__(cls, *args, **kwargs)
|
|
||||||
instance.keyOrder = []
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def __init__(self, data=None):
|
|
||||||
warnings.warn(
|
|
||||||
"SortedDict is deprecated and will be removed in Django 1.9.",
|
|
||||||
RemovedInDjango19Warning, stacklevel=2
|
|
||||||
)
|
|
||||||
if data is None or isinstance(data, dict):
|
|
||||||
data = data or []
|
|
||||||
super(SortedDict, self).__init__(data)
|
|
||||||
self.keyOrder = list(data) if data else []
|
|
||||||
else:
|
|
||||||
super(SortedDict, self).__init__()
|
|
||||||
super_set = super(SortedDict, self).__setitem__
|
|
||||||
for key, value in data:
|
|
||||||
# Take the ordering from first key
|
|
||||||
if key not in self:
|
|
||||||
self.keyOrder.append(key)
|
|
||||||
# But override with last value in data (dict() does this)
|
|
||||||
super_set(key, value)
|
|
||||||
|
|
||||||
def __deepcopy__(self, memo):
|
|
||||||
return self.__class__([(key, copy.deepcopy(value, memo))
|
|
||||||
for key, value in self.items()])
|
|
||||||
|
|
||||||
def __copy__(self):
|
|
||||||
# The Python's default copy implementation will alter the state
|
|
||||||
# of self. The reason for this seems complex but is likely related to
|
|
||||||
# subclassing dict.
|
|
||||||
return self.copy()
|
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
|
||||||
if key not in self:
|
|
||||||
self.keyOrder.append(key)
|
|
||||||
super(SortedDict, self).__setitem__(key, value)
|
|
||||||
|
|
||||||
def __delitem__(self, key):
|
|
||||||
super(SortedDict, self).__delitem__(key)
|
|
||||||
self.keyOrder.remove(key)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(self.keyOrder)
|
|
||||||
|
|
||||||
def __reversed__(self):
|
|
||||||
return reversed(self.keyOrder)
|
|
||||||
|
|
||||||
def pop(self, k, *args):
|
|
||||||
result = super(SortedDict, self).pop(k, *args)
|
|
||||||
try:
|
|
||||||
self.keyOrder.remove(k)
|
|
||||||
except ValueError:
|
|
||||||
# Key wasn't in the dictionary in the first place. No problem.
|
|
||||||
pass
|
|
||||||
return result
|
|
||||||
|
|
||||||
def popitem(self):
|
|
||||||
result = super(SortedDict, self).popitem()
|
|
||||||
self.keyOrder.remove(result[0])
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _iteritems(self):
|
|
||||||
for key in self.keyOrder:
|
|
||||||
yield key, self[key]
|
|
||||||
|
|
||||||
def _iterkeys(self):
|
|
||||||
for key in self.keyOrder:
|
|
||||||
yield key
|
|
||||||
|
|
||||||
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 [(k, self[k]) for k in self.keyOrder]
|
|
||||||
|
|
||||||
def keys(self):
|
|
||||||
return self.keyOrder[:]
|
|
||||||
|
|
||||||
def values(self):
|
|
||||||
return [self[k] for k in self.keyOrder]
|
|
||||||
|
|
||||||
def update(self, dict_):
|
|
||||||
for k, v in six.iteritems(dict_):
|
|
||||||
self[k] = v
|
|
||||||
|
|
||||||
def setdefault(self, key, default):
|
|
||||||
if key not in self:
|
|
||||||
self.keyOrder.append(key)
|
|
||||||
return super(SortedDict, self).setdefault(key, default)
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
"""Returns a copy of this object."""
|
|
||||||
# This way of initializing the copy means it works for subclasses, too.
|
|
||||||
return self.__class__(self)
|
|
||||||
|
|
||||||
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 six.iteritems(self))
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
super(SortedDict, self).clear()
|
|
||||||
self.keyOrder = []
|
|
||||||
|
|
||||||
|
|
||||||
class OrderedSet(object):
|
class OrderedSet(object):
|
||||||
|
|
|
@ -97,34 +97,6 @@ need to distinguish caches by the ``Accept-language`` header.
|
||||||
cache, this just means that we have to build the response once to get at
|
cache, this just means that we have to build the response once to get at
|
||||||
the Vary header and so at the list of headers to use for the cache key.
|
the Vary header and so at the list of headers to use for the cache key.
|
||||||
|
|
||||||
``django.utils.datastructures``
|
|
||||||
===============================
|
|
||||||
|
|
||||||
.. module:: django.utils.datastructures
|
|
||||||
:synopsis: Data structures that aren't in Python's standard library.
|
|
||||||
|
|
||||||
.. class:: SortedDict
|
|
||||||
|
|
||||||
.. deprecated:: 1.7
|
|
||||||
``SortedDict`` is deprecated and will be removed in Django 1.9. Use
|
|
||||||
:class:`collections.OrderedDict` instead.
|
|
||||||
|
|
||||||
The :class:`django.utils.datastructures.SortedDict` class is a dictionary
|
|
||||||
that keeps its keys in the order in which they're inserted.
|
|
||||||
|
|
||||||
Creating a new SortedDict
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Creating a new ``SortedDict`` must be done in a way where ordering is
|
|
||||||
guaranteed. For example::
|
|
||||||
|
|
||||||
SortedDict({'b': 1, 'a': 2, 'c': 3})
|
|
||||||
|
|
||||||
will not work. Passing in a basic Python ``dict`` could produce unreliable
|
|
||||||
results. Instead do::
|
|
||||||
|
|
||||||
SortedDict([('b', 1), ('a', 2), ('c', 3)])
|
|
||||||
|
|
||||||
``django.utils.dateparse``
|
``django.utils.dateparse``
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
|
@ -714,12 +714,12 @@ Data structures
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
``django.newforms.forms.SortedDictFromList`` was removed.
|
``django.newforms.forms.SortedDictFromList`` was removed.
|
||||||
:class:`django.utils.datastructures.SortedDict` can now be instantiated with
|
``django.utils.datastructures.SortedDict`` can now be instantiated with
|
||||||
a sequence of tuples.
|
a sequence of tuples.
|
||||||
|
|
||||||
To update your code:
|
To update your code:
|
||||||
|
|
||||||
1. Use :class:`django.utils.datastructures.SortedDict` wherever you were
|
1. Use ``django.utils.datastructures.SortedDict`` wherever you were
|
||||||
using ``django.newforms.forms.SortedDictFromList``.
|
using ``django.newforms.forms.SortedDictFromList``.
|
||||||
|
|
||||||
2. Because ``django.utils.datastructures.SortedDict.copy`` doesn't
|
2. Because ``django.utils.datastructures.SortedDict.copy`` doesn't
|
||||||
|
|
|
@ -1511,8 +1511,7 @@ Python versions, this module isn't useful anymore. It has been deprecated. Use
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
As :class:`~collections.OrderedDict` was added to the standard library in
|
As :class:`~collections.OrderedDict` was added to the standard library in
|
||||||
Python 2.7, :class:`~django.utils.datastructures.SortedDict` is no longer
|
Python 2.7, ``SortedDict`` is no longer needed and has been deprecated.
|
||||||
needed and has been deprecated.
|
|
||||||
|
|
||||||
Custom SQL location for models package
|
Custom SQL location for models package
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -181,7 +181,7 @@ versions of Python.
|
||||||
|
|
||||||
:meth:`dict.keys`, :meth:`dict.items` and :meth:`dict.values` return lists in
|
:meth:`dict.keys`, :meth:`dict.items` and :meth:`dict.values` return lists in
|
||||||
Python 2 and iterators in Python 3. :class:`~django.http.QueryDict` and the
|
Python 2 and iterators in Python 3. :class:`~django.http.QueryDict` and the
|
||||||
:class:`dict`-like classes defined in :mod:`django.utils.datastructures`
|
:class:`dict`-like classes defined in ``django.utils.datastructures``
|
||||||
behave likewise in Python 3.
|
behave likewise in Python 3.
|
||||||
|
|
||||||
six_ provides compatibility functions to work around this change:
|
six_ provides compatibility functions to work around this change:
|
||||||
|
|
|
@ -3,140 +3,13 @@ Tests for stuff in django.utils.datastructures.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import pickle
|
|
||||||
|
|
||||||
from django.test import SimpleTestCase, ignore_warnings
|
from django.test import SimpleTestCase
|
||||||
from django.utils.datastructures import (DictWrapper, ImmutableList,
|
from django.utils.datastructures import (DictWrapper, ImmutableList,
|
||||||
MultiValueDict, MultiValueDictKeyError, OrderedSet, SortedDict)
|
MultiValueDict, MultiValueDictKeyError, OrderedSet)
|
||||||
from django.utils.deprecation import RemovedInDjango19Warning
|
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
|
|
||||||
@ignore_warnings(category=RemovedInDjango19Warning)
|
|
||||||
class SortedDictTests(SimpleTestCase):
|
|
||||||
def setUp(self):
|
|
||||||
super(SortedDictTests, self).setUp()
|
|
||||||
self.d1 = SortedDict()
|
|
||||||
self.d1[7] = 'seven'
|
|
||||||
self.d1[1] = 'one'
|
|
||||||
self.d1[9] = 'nine'
|
|
||||||
|
|
||||||
self.d2 = SortedDict()
|
|
||||||
self.d2[1] = 'one'
|
|
||||||
self.d2[9] = 'nine'
|
|
||||||
self.d2[0] = 'nil'
|
|
||||||
self.d2[7] = 'seven'
|
|
||||||
|
|
||||||
def test_basic_methods(self):
|
|
||||||
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 its place. """
|
|
||||||
self.d1[1] = 'ONE'
|
|
||||||
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(list(six.iterkeys(self.d1)), [7, 1, 9, 0])
|
|
||||||
|
|
||||||
def test_delete_and_insert(self):
|
|
||||||
"""
|
|
||||||
Deleting an item, then inserting the same key again will place it
|
|
||||||
at the end.
|
|
||||||
"""
|
|
||||||
del self.d2[7]
|
|
||||||
self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0])
|
|
||||||
self.d2[7] = 'lucky number 7'
|
|
||||||
self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0, 7])
|
|
||||||
|
|
||||||
if six.PY2:
|
|
||||||
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):
|
|
||||||
"""
|
|
||||||
Initialising a SortedDict with two keys will just take the first one.
|
|
||||||
|
|
||||||
A real dict will actually take the second value so we will too, but
|
|
||||||
we'll keep the ordering from the first key found.
|
|
||||||
"""
|
|
||||||
tuples = ((2, 'two'), (1, 'one'), (2, 'second-two'))
|
|
||||||
d = SortedDict(tuples)
|
|
||||||
|
|
||||||
self.assertEqual(list(six.iterkeys(d)), [2, 1])
|
|
||||||
|
|
||||||
real_dict = dict(tuples)
|
|
||||||
self.assertEqual(sorted(six.itervalues(real_dict)), ['one', 'second-two'])
|
|
||||||
|
|
||||||
# Here the order of SortedDict values *is* what we are testing
|
|
||||||
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(list(six.iterkeys(self.d1)), list(six.iterkeys(self.d1.copy())))
|
|
||||||
|
|
||||||
def test_append(self):
|
|
||||||
self.d1[13] = 'thirteen'
|
|
||||||
self.assertEqual(
|
|
||||||
repr(self.d1),
|
|
||||||
"{7: 'seven', 1: 'one', 9: 'nine', 13: 'thirteen'}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_pop(self):
|
|
||||||
self.assertEqual(self.d1.pop(1, 'missing'), 'one')
|
|
||||||
self.assertEqual(self.d1.pop(1, 'missing'), 'missing')
|
|
||||||
|
|
||||||
# We don't know which item will be popped in popitem(), so we'll
|
|
||||||
# just check that the number of keys has decreased.
|
|
||||||
l = len(self.d1)
|
|
||||||
self.d1.popitem()
|
|
||||||
self.assertEqual(l - len(self.d1), 1)
|
|
||||||
|
|
||||||
def test_dict_equality(self):
|
|
||||||
d = SortedDict((i, i) for i in range(3))
|
|
||||||
self.assertEqual(d, {0: 0, 1: 1, 2: 2})
|
|
||||||
|
|
||||||
def test_tuple_init(self):
|
|
||||||
d = SortedDict(((1, "one"), (0, "zero"), (2, "two")))
|
|
||||||
self.assertEqual(repr(d), "{1: 'one', 0: 'zero', 2: 'two'}")
|
|
||||||
|
|
||||||
def test_pickle(self):
|
|
||||||
self.assertEqual(
|
|
||||||
pickle.loads(pickle.dumps(self.d1, 2)),
|
|
||||||
{7: 'seven', 1: 'one', 9: 'nine'}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_copy(self):
|
|
||||||
orig = SortedDict(((1, "one"), (0, "zero"), (2, "two")))
|
|
||||||
copied = copy.copy(orig)
|
|
||||||
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()
|
|
||||||
self.assertEqual(self.d1, {})
|
|
||||||
self.assertEqual(self.d1.keyOrder, [])
|
|
||||||
|
|
||||||
def test_reversed(self):
|
|
||||||
self.assertEqual(list(self.d1), [7, 1, 9])
|
|
||||||
self.assertEqual(list(self.d2), [1, 9, 0, 7])
|
|
||||||
self.assertEqual(list(reversed(self.d1)), [9, 1, 7])
|
|
||||||
self.assertEqual(list(reversed(self.d2)), [7, 0, 9, 1])
|
|
||||||
|
|
||||||
|
|
||||||
class OrderedSetTests(SimpleTestCase):
|
class OrderedSetTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_bool(self):
|
def test_bool(self):
|
||||||
|
|
Loading…
Reference in New Issue