Browse Source

Optimized handling case-insensitive mappings.

Elements yielded by _destruct_iterable_mapping_values are always
unpacked. Since unpacking can be done with any iterable, there is no
need to convert elements to tuples. Also, such elements can be used
directly in for loops, creating a dictionary of them is excessive.

Co-authored-by: Nick Pope <nick@nickpope.me.uk>
Illia Volochii 3 years ago
parent
commit
bc4c7e5d68
2 changed files with 20 additions and 22 deletions
  1. 2 7
      django/http/response.py
  2. 18 15
      django/utils/datastructures.py

+ 2 - 7
django/http/response.py

@@ -5,7 +5,6 @@ import os
 import re
 import sys
 import time
-from collections.abc import Mapping
 from email.header import Header
 from http.client import responses
 from urllib.parse import quote, urlparse
@@ -16,9 +15,7 @@ from django.core.exceptions import DisallowedRedirect
 from django.core.serializers.json import DjangoJSONEncoder
 from django.http.cookie import SimpleCookie
 from django.utils import timezone
-from django.utils.datastructures import (
-    CaseInsensitiveMapping, _destruct_iterable_mapping_values,
-)
+from django.utils.datastructures import CaseInsensitiveMapping
 from django.utils.encoding import iri_to_uri
 from django.utils.http import http_date
 from django.utils.regex_helper import _lazy_re_compile
@@ -32,10 +29,8 @@ class ResponseHeaders(CaseInsensitiveMapping):
         Populate the initial data using __setitem__ to ensure values are
         correctly encoded.
         """
-        if not isinstance(data, Mapping):
-            data = {k: v for k, v in _destruct_iterable_mapping_values(data)}
         self._store = {}
-        for header, value in data.items():
+        for header, value in self._unpack_items(data):
             self[header] = value
 
     def _convert_to_charset(self, value, charset, mime_encode=False):

+ 18 - 15
django/utils/datastructures.py

@@ -284,18 +284,6 @@ class DictWrapper(dict):
         return value
 
 
-def _destruct_iterable_mapping_values(data):
-    for i, elem in enumerate(data):
-        if len(elem) != 2:
-            raise ValueError(
-                'dictionary update sequence element #{} has '
-                'length {}; 2 is required.'.format(i, len(elem))
-            )
-        if not isinstance(elem[0], str):
-            raise ValueError('Element key %r invalid, only strings are allowed' % elem[0])
-        yield tuple(elem)
-
-
 class CaseInsensitiveMapping(Mapping):
     """
     Mapping allowing case-insensitive key lookups. Original case of keys is
@@ -315,9 +303,7 @@ class CaseInsensitiveMapping(Mapping):
     """
 
     def __init__(self, data):
-        if not isinstance(data, Mapping):
-            data = {k: v for k, v in _destruct_iterable_mapping_values(data)}
-        self._store = {k.lower(): (k, v) for k, v in data.items()}
+        self._store = {k.lower(): (k, v) for k, v in self._unpack_items(data)}
 
     def __getitem__(self, key):
         return self._store[key.lower()][1]
@@ -340,3 +326,20 @@ class CaseInsensitiveMapping(Mapping):
 
     def copy(self):
         return self
+
+    @staticmethod
+    def _unpack_items(data):
+        if isinstance(data, Mapping):
+            yield from data.items()
+            return
+        for i, elem in enumerate(data):
+            if len(elem) != 2:
+                raise ValueError(
+                    'dictionary update sequence element #{} has length {}; '
+                    '2 is required.'.format(i, len(elem))
+                )
+            if not isinstance(elem[0], str):
+                raise ValueError(
+                    'Element key %r invalid, only strings are allowed' % elem[0]
+                )
+            yield elem