Commit 37b7776a authored by Tim Graham's avatar Tim Graham
Browse files

Removed django.utils.datastructures.MergeDict per deprecation timeline; refs #18659.

parent 75f107b8
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ from itertools import chain

from django.conf import settings
from django.forms.utils import flatatt, to_current_timezone
from django.utils.datastructures import MultiValueDict, MergeDict
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.html import conditional_escape, format_html
from django.utils.translation import ugettext_lazy
@@ -319,7 +319,7 @@ class MultipleHiddenInput(HiddenInput):
        return mark_safe('\n'.join(inputs))

    def value_from_datadict(self, data, files, name):
        if isinstance(data, (MultiValueDict, MergeDict)):
        if isinstance(data, MultiValueDict):
            return data.getlist(name)
        return data.get(name, None)

@@ -587,7 +587,7 @@ class SelectMultiple(Select):
        return mark_safe('\n'.join(output))

    def value_from_datadict(self, data, files, name):
        if isinstance(data, (MultiValueDict, MergeDict)):
        if isinstance(data, MultiValueDict):
            return data.getlist(name)
        return data.get(name, None)

+0 −115
Original line number Diff line number Diff line
@@ -6,121 +6,6 @@ from django.utils import six
from django.utils.deprecation import RemovedInDjango19Warning


class MergeDict(object):
    """
    A simple class for creating new "virtual" dictionaries that actually look
    up values in more than one dictionary, passed in the constructor.

    If a key appears in more than one of the given dictionaries, only the
    first occurrence will be used.
    """
    def __init__(self, *dicts):
        warnings.warn('`MergeDict` is deprecated, use `dict.update()` '
                      'instead.', RemovedInDjango19Warning, 2)
        self.dicts = dicts

    def __bool__(self):
        return any(self.dicts)

    def __nonzero__(self):
        return type(self).__bool__(self)

    def __getitem__(self, key):
        for dict_ in self.dicts:
            try:
                return dict_[key]
            except KeyError:
                pass
        raise KeyError(key)

    def __copy__(self):
        return self.__class__(*self.dicts)

    def get(self, key, default=None):
        try:
            return self[key]
        except KeyError:
            return default

    # This is used by MergeDicts of MultiValueDicts.
    def getlist(self, key):
        for dict_ in self.dicts:
            if key in dict_:
                return dict_.getlist(key)
        return []

    def _iteritems(self):
        seen = set()
        for dict_ in self.dicts:
            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():
            yield k

    def _itervalues(self):
        for k, v in self._iteritems():
            yield v

    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 has_key(self, key):
        for dict_ in self.dicts:
            if key in dict_:
                return True
        return False

    __contains__ = has_key

    __iter__ = _iterkeys

    def copy(self):
        """Returns a copy of this object."""
        return self.__copy__()

    def __str__(self):
        '''
        Returns something like

            "{'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}"

        instead of the generic "<object meta-data>" inherited from object.
        '''
        return str(dict(self.items()))

    def __repr__(self):
        '''
        Returns something like

            MergeDict({'key1': 'val1', 'key2': 'val2'}, {'key3': 'val3'})

        instead of generic "<object meta-data>" inherited from object.
        '''
        dictreprs = ', '.join(repr(d) for d in self.dicts)
        return '%s(%s)' % (self.__class__.__name__, dictreprs)


class SortedDict(dict):
    """
    A dictionary that keeps its keys in the order in which they're inserted.
+4 −12
Original line number Diff line number Diff line
@@ -18,10 +18,9 @@ from django.forms import (
from django.forms.utils import ErrorList
from django.http import QueryDict
from django.template import Template, Context
from django.test import TestCase, ignore_warnings
from django.test import TestCase
from django.test.utils import str_prefix
from django.utils.datastructures import MultiValueDict, MergeDict
from django.utils.deprecation import RemovedInDjango19Warning
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.safestring import mark_safe, SafeData
@@ -552,11 +551,9 @@ class FormsTestCase(TestCase):
<li><label for="composers_id_1"><input type="checkbox" name="composers" value="P" id="composers_id_1" /> Paul McCartney</label></li>
</ul>""")

    @ignore_warnings(category=RemovedInDjango19Warning)  # MergeDict deprecation
    def test_multiple_choice_list_data(self):
        # Data for a MultipleChoiceField should be a list. QueryDict, MultiValueDict and
        # MergeDict (when created as a merge of MultiValueDicts) conveniently work with
        # this.
        # Data for a MultipleChoiceField should be a list. QueryDict and
        # MultiValueDict conveniently work with this.
        class SongForm(Form):
            name = CharField()
            composers = MultipleChoiceField(choices=[('J', 'John Lennon'), ('P', 'Paul McCartney')], widget=CheckboxSelectMultiple)
@@ -573,11 +570,6 @@ class FormsTestCase(TestCase):
        f = SongForm(data)
        self.assertEqual(f.errors, {})

        # MergeDict is deprecated, but is supported until removed.
        data = MergeDict(MultiValueDict(dict(name=['Yesterday'], composers=['J', 'P'])))
        f = SongForm(data)
        self.assertEqual(f.errors, {})

    def test_multiple_hidden(self):
        class SongForm(Form):
            name = CharField()
+1 −72
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ import pickle

from django.test import SimpleTestCase, ignore_warnings
from django.utils.datastructures import (DictWrapper, ImmutableList,
    MultiValueDict, MultiValueDictKeyError, MergeDict, OrderedSet, SortedDict)
    MultiValueDict, MultiValueDictKeyError, OrderedSet, SortedDict)
from django.utils.deprecation import RemovedInDjango19Warning
from django.utils import six

@@ -137,77 +137,6 @@ class SortedDictTests(SimpleTestCase):
        self.assertEqual(list(reversed(self.d2)), [7, 0, 9, 1])


@ignore_warnings(category=RemovedInDjango19Warning)
class MergeDictTests(SimpleTestCase):

    def test_simple_mergedict(self):
        d1 = {'chris': 'cool', 'camri': 'cute', 'cotton': 'adorable',
              'tulip': 'snuggable', 'twoofme': 'firstone'}

        d2 = {'chris2': 'cool2', 'camri2': 'cute2', 'cotton2': 'adorable2',
              'tulip2': 'snuggable2'}

        d3 = {'chris3': 'cool3', 'camri3': 'cute3', 'cotton3': 'adorable3',
              'tulip3': 'snuggable3'}

        md = MergeDict(d1, d2, d3)

        self.assertEqual(md['chris'], 'cool')
        self.assertEqual(md['camri'], 'cute')
        self.assertEqual(md['twoofme'], 'firstone')

        md2 = md.copy()
        self.assertEqual(md2['chris'], 'cool')

    def test_mergedict_merges_multivaluedict(self):
        """ MergeDict can merge MultiValueDicts """

        multi1 = MultiValueDict({'key1': ['value1'],
                                 'key2': ['value2', 'value3']})

        multi2 = MultiValueDict({'key2': ['value4'],
                                 'key4': ['value5', 'value6']})

        mm = MergeDict(multi1, multi2)

        # Although 'key2' appears in both dictionaries,
        # only the first value is used.
        self.assertEqual(mm.getlist('key2'), ['value2', 'value3'])
        self.assertEqual(mm.getlist('key4'), ['value5', 'value6'])
        self.assertEqual(mm.getlist('undefined'), [])

        self.assertEqual(sorted(six.iterkeys(mm)), ['key1', 'key2', 'key4'])
        self.assertEqual(len(list(six.itervalues(mm))), 3)

        self.assertIn('value1', six.itervalues(mm))

        self.assertEqual(
            sorted(six.iteritems(mm), key=lambda k: k[0]),
            [('key1', 'value1'), ('key2', 'value3'), ('key4', 'value6')]
        )

        self.assertEqual(
            [(k, mm.getlist(k)) for k in sorted(mm)],
            [('key1', ['value1']),
             ('key2', ['value2', 'value3']),
             ('key4', ['value5', 'value6'])]
        )

    def test_bool_casting(self):
        empty = MergeDict({}, {}, {})
        not_empty = MergeDict({}, {}, {"key": "value"})
        self.assertFalse(empty)
        self.assertTrue(not_empty)

    def test_key_error(self):
        """
        Test that the message of KeyError contains the missing key name.
        """
        d1 = MergeDict({'key1': 42})
        with six.assertRaisesRegex(self, KeyError, 'key2'):
            d1['key2']


class OrderedSetTests(SimpleTestCase):

    def test_bool(self):