Ticket #2970: http_response_insensitive.3.patch

File http_response_insensitive.3.patch, 7.2 KB (added by Chris Beaven, 17 years ago)

Tests and included CaseInsensitiveDict

  • django/http/__init__.py

     
    22from Cookie import SimpleCookie
    33from pprint import pformat
    44from urllib import urlencode, quote
    5 from django.utils.datastructures import MultiValueDict
     5from django.utils.datastructures import MultiValueDict, CaseInsensitiveDict
    66
    77RESERVED_CHARS="!*'();:@&=+$,/?%#[]"
    88
     
    188188    def __getitem__(self, header):
    189189        return self.headers[header]
    190190
     191    def get_headers(self):
     192        return self._headers
     193   
     194    def set_headers(self, headers):
     195        self._headers = CaseInsensitiveDict(headers)
     196       
     197    headers = property(get_headers, set_headers)
     198
    191199    def has_header(self, header):
    192200        "Case-insensitive check for a header"
    193         header = header.lower()
    194         for key in self.headers.keys():
    195             if key.lower() == header:
    196                 return True
    197         return False
     201        return self.headers.has_key(header)
    198202
    199203    def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None):
    200204        self.cookies[key] = value
  • django/utils/datastructures.py

     
    242242                current[bits[-1]] = v
    243243            except TypeError: # Special-case if current isn't a dict.
    244244                current = {bits[-1] : v}
     245
     246class CaseInsensitiveDict(dict):
     247    """
     248    A dictionary which uses case-insensitive keys. Original case is preserved,
     249    but can be read or overwritten by the same key of any mix of upper and
     250    lower case.
     251    """
     252    def __init__(self, *kw, **kwargs):
     253        super(CaseInsensitiveDict, self).__init__(*kw, **kwargs)
     254        # Set up case insensitive keys dict for future reference.
     255        keys = {}
     256        for key in self.keys():
     257            if isinstance(key, basestring):
     258                keys[key.lower()] = key
     259        self._case_insensitive_keys = keys
     260
     261    def _get_case_sensitive_key(self, key):
     262        if isinstance(key, basestring):
     263            key = self._case_insensitive_keys.get(key.lower(), key)
     264        return key
     265
     266    def __getitem__(self, key):
     267        key = self._get_case_sensitive_key(key)
     268        return super(CaseInsensitiveDict, self).__getitem__(key)
     269
     270    def __setitem__(self, key, value):
     271        if isinstance(key, basestring):
     272            key_lower = key.lower()
     273            if key_lower in self._case_insensitive_keys.keys():
     274                # Delete old key (since it may be a different case).
     275                old_key = self._case_insensitive_keys[key_lower]
     276                super(CaseInsensitiveDict, self).__delitem__(old_key)
     277            # Save new key in case insensitive dict.
     278            self._case_insensitive_keys[key_lower] = key
     279        return super(CaseInsensitiveDict, self).__setitem__(key, value)
     280
     281    def __delitem__(self, key):
     282        key = self._get_case_sensitive_key(key)
     283        if isinstance(key, basestring):
     284            # Delete key from case insensitive dict.
     285            del self._case_insensitive_keys[key.lower()]
     286        return super(CaseInsensitiveDict, self).__delitem__(key)
     287
     288    def __contains__(self, key):
     289        key = self._get_case_sensitive_key(key)
     290        return super(CaseInsensitiveDict, self).__contains__(key)
     291
     292    def get(self, key, default=None):
     293        key = self._get_case_sensitive_key(key)
     294        return super(CaseInsensitiveDict, self).get(key, default)
     295
     296    def has_key(self, key):
     297        key = self._get_case_sensitive_key(key)
     298        return super(CaseInsensitiveDict, self).has_key(key)
     299
     300    def pop(self, key):
     301        key = self._get_case_sensitive_key(key)
     302        # Delete key from case insensitive dict. Could raise KeyError, but
     303        # that's what is raised on a non-existant .pop(key) anyway.
     304        del self._case_insensitive_keys[key.lower()]
     305        return super(CaseInsensitiveDict, self).pop(key)
     306
     307    def update(self, dict=None, **kwargs):
     308        if dict is None:
     309            # Handle it the alternate way introduced in Python 2.4
     310            dict = kwargs
     311        if dict:
     312            # Check for keys in new dict which may differ to the current keys.
     313            for key in dict.keys():
     314                existing_key = self._get_case_sensitive_key(key)
     315                if key != existing_key:
     316                    # Delete existing key (with different case).
     317                    del self[existing_key]
     318                    # Change existing case insensitive key to match new key case.
     319                    self._case_insensitive_keys[key.lower()] = key
     320        return super(CaseInsensitiveDict, self).update(dict)
     321
     322    def clear(self):
     323        # Reset case insensitive dict
     324        self._case_insensitive_keys = {}
     325        return super(CaseInsensitiveDict, self).clear()
     326
     327    def setdefault(self, key, default=None):
     328        if not self.has_key(key):
     329            # Update case insensitive dict, since dict.setdefault() doesn't
     330            # use internal __setitem__ method.
     331            self._case_insensitive_keys[key.lower()] = key
     332        return super(CaseInsensitiveDict, self).setdefault(key, default)
  • tests/regressiontests/datastructures/tests.py

     
     1r"""
     2#########################
     3# CASE INSENSITIVE DICT #
     4#########################
     5>>> from django.utils.datastructures import CaseInsensitiveDict
     6>>> d = CaseInsensitiveDict()
     7
     8# Non string keys should still work exactly the same.
     9>>> d[5] = 'five'
     10>>> d[5]
     11'five'
     12>>> d.get(5)
     13'five'
     14>>> d.has_key(5)
     15True
     16
     17# String keys will preserve case, but be replaced or read with any case.
     18>>> d['one'] = 1
     19>>> d['TWO'] = 2
     20>>> d['Three'] = 3
     21
     22# Basic tests.
     23>>> d['one'], d['two'], d['three']
     24(1, 2, 3)
     25>>> d['ONE'], d['TWO'], d['THREE']
     26(1, 2, 3)
     27>>> d['oNe'], d['tWo'], d['tHree']
     28(1, 2, 3)
     29
     30# Replace a key.
     31>>> d['thrEE'] = 33
     32>>> d['Three']
     3333
     34>>> len(d.keys())
     354
     36>>> d[5] = 'FIVE'
     37>>> d[5]
     38'FIVE'
     39
     40# Check .get()
     41>>> d.get('four', 'does not exist')
     42'does not exist'
     43>>> d.get(5)
     44'FIVE'
     45
     46>>> d.has_key('one')
     47True
     48>>> d.has_key('onE')
     49True
     50>>> d.has_key('four')
     51False
     52>>> d.has_key(5)
     53True
     54>>> d.has_key(6)
     55False
     56
     57>>> d.setdefault(5)
     58'FIVE'
     59>>> d.setdefault('fOUr', 4)
     604
     61>>> keys = d.keys()
     62>>> keys.sort()
     63>>> keys
     64[5, 'TWO', 'fOUr', 'one', 'thrEE']
     65
     66>>> 'fouR' in d
     67True
     68>>> 'six' in d
     69False
     70
     71>>> del d['THREE']
     72>>> len(d)
     734
     74
     75>>> d.pop('tWo')
     762
     77>>> len(d)
     783
     79
     80>>> d.update({'ONE': 111, 'Two': 222, 'three': 333})
     81>>> values = d.values()
     82>>> values.sort()
     83>>> values
     84[4, 111, 222, 333, 'FIVE']
     85
     86>>> d.clear()
     87>>> d
     88{}
     89"""
Back to Top