diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 275f2a428a..6c60675d38 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -1,132 +1,7 @@ import copy -import warnings from collections import OrderedDict 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): diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index 55184c277b..d1881bb830 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -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 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`` ========================== diff --git a/docs/releases/1.0-porting-guide.txt b/docs/releases/1.0-porting-guide.txt index a3c1a95702..a8b4ccaa26 100644 --- a/docs/releases/1.0-porting-guide.txt +++ b/docs/releases/1.0-porting-guide.txt @@ -714,12 +714,12 @@ Data structures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``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. 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``. 2. Because ``django.utils.datastructures.SortedDict.copy`` doesn't diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index b5888bc52a..2592d64983 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -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 -Python 2.7, :class:`~django.utils.datastructures.SortedDict` is no longer -needed and has been deprecated. +Python 2.7, ``SortedDict`` is no longer needed and has been deprecated. Custom SQL location for models package ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/python3.txt b/docs/topics/python3.txt index 2f12809163..5dc2cf464c 100644 --- a/docs/topics/python3.txt +++ b/docs/topics/python3.txt @@ -181,7 +181,7 @@ versions of Python. :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 -:class:`dict`-like classes defined in :mod:`django.utils.datastructures` +:class:`dict`-like classes defined in ``django.utils.datastructures`` behave likewise in Python 3. six_ provides compatibility functions to work around this change: diff --git a/tests/utils_tests/test_datastructures.py b/tests/utils_tests/test_datastructures.py index fafb0cfdce..425186c578 100644 --- a/tests/utils_tests/test_datastructures.py +++ b/tests/utils_tests/test_datastructures.py @@ -3,140 +3,13 @@ Tests for stuff in django.utils.datastructures. """ import copy -import pickle -from django.test import SimpleTestCase, ignore_warnings +from django.test import SimpleTestCase from django.utils.datastructures import (DictWrapper, ImmutableList, - MultiValueDict, MultiValueDictKeyError, OrderedSet, SortedDict) -from django.utils.deprecation import RemovedInDjango19Warning + MultiValueDict, MultiValueDictKeyError, OrderedSet) 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): def test_bool(self):