web/lib/django/utils/datastructures.py
changeset 0 0d40e90630ef
child 29 cc9b7e14412b
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 class MergeDict(object):
       
     2     """
       
     3     A simple class for creating new "virtual" dictionaries that actually look
       
     4     up values in more than one dictionary, passed in the constructor.
       
     5 
       
     6     If a key appears in more than one of the given dictionaries, only the
       
     7     first occurrence will be used.
       
     8     """
       
     9     def __init__(self, *dicts):
       
    10         self.dicts = dicts
       
    11 
       
    12     def __getitem__(self, key):
       
    13         for dict_ in self.dicts:
       
    14             try:
       
    15                 return dict_[key]
       
    16             except KeyError:
       
    17                 pass
       
    18         raise KeyError
       
    19 
       
    20     def __copy__(self):
       
    21         return self.__class__(*self.dicts)
       
    22 
       
    23     def get(self, key, default=None):
       
    24         try:
       
    25             return self[key]
       
    26         except KeyError:
       
    27             return default
       
    28 
       
    29     def getlist(self, key):
       
    30         for dict_ in self.dicts:
       
    31             if key in dict_.keys():
       
    32                 return dict_.getlist(key)
       
    33         return []
       
    34 
       
    35     def items(self):
       
    36         item_list = []
       
    37         for dict_ in self.dicts:
       
    38             item_list.extend(dict_.items())
       
    39         return item_list
       
    40 
       
    41     def has_key(self, key):
       
    42         for dict_ in self.dicts:
       
    43             if key in dict_:
       
    44                 return True
       
    45         return False
       
    46 
       
    47     __contains__ = has_key
       
    48 
       
    49     def copy(self):
       
    50         """Returns a copy of this object."""
       
    51         return self.__copy__()
       
    52 
       
    53 class SortedDict(dict):
       
    54     """
       
    55     A dictionary that keeps its keys in the order in which they're inserted.
       
    56     """
       
    57     def __new__(cls, *args, **kwargs):
       
    58         instance = super(SortedDict, cls).__new__(cls, *args, **kwargs)
       
    59         instance.keyOrder = []
       
    60         return instance
       
    61 
       
    62     def __init__(self, data=None):
       
    63         if data is None:
       
    64             data = {}
       
    65         super(SortedDict, self).__init__(data)
       
    66         if isinstance(data, dict):
       
    67             self.keyOrder = data.keys()
       
    68         else:
       
    69             self.keyOrder = []
       
    70             for key, value in data:
       
    71                 if key not in self.keyOrder:
       
    72                     self.keyOrder.append(key)
       
    73 
       
    74     def __deepcopy__(self, memo):
       
    75         from copy import deepcopy
       
    76         return self.__class__([(key, deepcopy(value, memo))
       
    77                                for key, value in self.iteritems()])
       
    78 
       
    79     def __setitem__(self, key, value):
       
    80         super(SortedDict, self).__setitem__(key, value)
       
    81         if key not in self.keyOrder:
       
    82             self.keyOrder.append(key)
       
    83 
       
    84     def __delitem__(self, key):
       
    85         super(SortedDict, self).__delitem__(key)
       
    86         self.keyOrder.remove(key)
       
    87 
       
    88     def __iter__(self):
       
    89         for k in self.keyOrder:
       
    90             yield k
       
    91 
       
    92     def pop(self, k, *args):
       
    93         result = super(SortedDict, self).pop(k, *args)
       
    94         try:
       
    95             self.keyOrder.remove(k)
       
    96         except ValueError:
       
    97             # Key wasn't in the dictionary in the first place. No problem.
       
    98             pass
       
    99         return result
       
   100 
       
   101     def popitem(self):
       
   102         result = super(SortedDict, self).popitem()
       
   103         self.keyOrder.remove(result[0])
       
   104         return result
       
   105 
       
   106     def items(self):
       
   107         return zip(self.keyOrder, self.values())
       
   108 
       
   109     def iteritems(self):
       
   110         for key in self.keyOrder:
       
   111             yield key, super(SortedDict, self).__getitem__(key)
       
   112 
       
   113     def keys(self):
       
   114         return self.keyOrder[:]
       
   115 
       
   116     def iterkeys(self):
       
   117         return iter(self.keyOrder)
       
   118 
       
   119     def values(self):
       
   120         return map(super(SortedDict, self).__getitem__, self.keyOrder)
       
   121 
       
   122     def itervalues(self):
       
   123         for key in self.keyOrder:
       
   124             yield super(SortedDict, self).__getitem__(key)
       
   125 
       
   126     def update(self, dict_):
       
   127         for k, v in dict_.items():
       
   128             self.__setitem__(k, v)
       
   129 
       
   130     def setdefault(self, key, default):
       
   131         if key not in self.keyOrder:
       
   132             self.keyOrder.append(key)
       
   133         return super(SortedDict, self).setdefault(key, default)
       
   134 
       
   135     def value_for_index(self, index):
       
   136         """Returns the value of the item at the given zero-based index."""
       
   137         return self[self.keyOrder[index]]
       
   138 
       
   139     def insert(self, index, key, value):
       
   140         """Inserts the key, value pair before the item with the given index."""
       
   141         if key in self.keyOrder:
       
   142             n = self.keyOrder.index(key)
       
   143             del self.keyOrder[n]
       
   144             if n < index:
       
   145                 index -= 1
       
   146         self.keyOrder.insert(index, key)
       
   147         super(SortedDict, self).__setitem__(key, value)
       
   148 
       
   149     def copy(self):
       
   150         """Returns a copy of this object."""
       
   151         # This way of initializing the copy means it works for subclasses, too.
       
   152         obj = self.__class__(self)
       
   153         obj.keyOrder = self.keyOrder[:]
       
   154         return obj
       
   155 
       
   156     def __repr__(self):
       
   157         """
       
   158         Replaces the normal dict.__repr__ with a version that returns the keys
       
   159         in their sorted order.
       
   160         """
       
   161         return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
       
   162 
       
   163     def clear(self):
       
   164         super(SortedDict, self).clear()
       
   165         self.keyOrder = []
       
   166 
       
   167 class MultiValueDictKeyError(KeyError):
       
   168     pass
       
   169 
       
   170 class MultiValueDict(dict):
       
   171     """
       
   172     A subclass of dictionary customized to handle multiple values for the
       
   173     same key.
       
   174 
       
   175     >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
       
   176     >>> d['name']
       
   177     'Simon'
       
   178     >>> d.getlist('name')
       
   179     ['Adrian', 'Simon']
       
   180     >>> d.get('lastname', 'nonexistent')
       
   181     'nonexistent'
       
   182     >>> d.setlist('lastname', ['Holovaty', 'Willison'])
       
   183 
       
   184     This class exists to solve the irritating problem raised by cgi.parse_qs,
       
   185     which returns a list for every key, even though most Web forms submit
       
   186     single name-value pairs.
       
   187     """
       
   188     def __init__(self, key_to_list_mapping=()):
       
   189         super(MultiValueDict, self).__init__(key_to_list_mapping)
       
   190 
       
   191     def __repr__(self):
       
   192         return "<%s: %s>" % (self.__class__.__name__,
       
   193                              super(MultiValueDict, self).__repr__())
       
   194 
       
   195     def __getitem__(self, key):
       
   196         """
       
   197         Returns the last data value for this key, or [] if it's an empty list;
       
   198         raises KeyError if not found.
       
   199         """
       
   200         try:
       
   201             list_ = super(MultiValueDict, self).__getitem__(key)
       
   202         except KeyError:
       
   203             raise MultiValueDictKeyError, "Key %r not found in %r" % (key, self)
       
   204         try:
       
   205             return list_[-1]
       
   206         except IndexError:
       
   207             return []
       
   208 
       
   209     def __setitem__(self, key, value):
       
   210         super(MultiValueDict, self).__setitem__(key, [value])
       
   211 
       
   212     def __copy__(self):
       
   213         return self.__class__(super(MultiValueDict, self).items())
       
   214 
       
   215     def __deepcopy__(self, memo=None):
       
   216         import copy
       
   217         if memo is None:
       
   218             memo = {}
       
   219         result = self.__class__()
       
   220         memo[id(self)] = result
       
   221         for key, value in dict.items(self):
       
   222             dict.__setitem__(result, copy.deepcopy(key, memo),
       
   223                              copy.deepcopy(value, memo))
       
   224         return result
       
   225     
       
   226     def __getstate__(self):
       
   227         obj_dict = self.__dict__.copy()
       
   228         obj_dict['_data'] = dict([(k, self.getlist(k)) for k in self])
       
   229         return obj_dict
       
   230     
       
   231     def __setstate__(self, obj_dict):
       
   232         data = obj_dict.pop('_data', {})
       
   233         for k, v in data.items():
       
   234             self.setlist(k, v)
       
   235         self.__dict__.update(obj_dict)
       
   236         
       
   237     def get(self, key, default=None):
       
   238         """
       
   239         Returns the last data value for the passed key. If key doesn't exist
       
   240         or value is an empty list, then default is returned.
       
   241         """
       
   242         try:
       
   243             val = self[key]
       
   244         except KeyError:
       
   245             return default
       
   246         if val == []:
       
   247             return default
       
   248         return val
       
   249 
       
   250     def getlist(self, key):
       
   251         """
       
   252         Returns the list of values for the passed key. If key doesn't exist,
       
   253         then an empty list is returned.
       
   254         """
       
   255         try:
       
   256             return super(MultiValueDict, self).__getitem__(key)
       
   257         except KeyError:
       
   258             return []
       
   259 
       
   260     def setlist(self, key, list_):
       
   261         super(MultiValueDict, self).__setitem__(key, list_)
       
   262 
       
   263     def setdefault(self, key, default=None):
       
   264         if key not in self:
       
   265             self[key] = default
       
   266         return self[key]
       
   267 
       
   268     def setlistdefault(self, key, default_list=()):
       
   269         if key not in self:
       
   270             self.setlist(key, default_list)
       
   271         return self.getlist(key)
       
   272 
       
   273     def appendlist(self, key, value):
       
   274         """Appends an item to the internal list associated with key."""
       
   275         self.setlistdefault(key, [])
       
   276         super(MultiValueDict, self).__setitem__(key, self.getlist(key) + [value])
       
   277 
       
   278     def items(self):
       
   279         """
       
   280         Returns a list of (key, value) pairs, where value is the last item in
       
   281         the list associated with the key.
       
   282         """
       
   283         return [(key, self[key]) for key in self.keys()]
       
   284 
       
   285     def iteritems(self):
       
   286         """
       
   287         Yields (key, value) pairs, where value is the last item in the list
       
   288         associated with the key.
       
   289         """
       
   290         for key in self.keys():
       
   291             yield (key, self[key])
       
   292 
       
   293     def lists(self):
       
   294         """Returns a list of (key, list) pairs."""
       
   295         return super(MultiValueDict, self).items()
       
   296 
       
   297     def iterlists(self):
       
   298         """Yields (key, list) pairs."""
       
   299         return super(MultiValueDict, self).iteritems()
       
   300 
       
   301     def values(self):
       
   302         """Returns a list of the last value on every key list."""
       
   303         return [self[key] for key in self.keys()]
       
   304         
       
   305     def itervalues(self):
       
   306         """Yield the last value on every key list."""
       
   307         for key in self.iterkeys():
       
   308             yield self[key]
       
   309     
       
   310     def copy(self):
       
   311         """Returns a copy of this object."""
       
   312         return self.__deepcopy__()
       
   313 
       
   314     def update(self, *args, **kwargs):
       
   315         """
       
   316         update() extends rather than replaces existing key lists.
       
   317         Also accepts keyword args.
       
   318         """
       
   319         if len(args) > 1:
       
   320             raise TypeError, "update expected at most 1 arguments, got %d" % len(args)
       
   321         if args:
       
   322             other_dict = args[0]
       
   323             if isinstance(other_dict, MultiValueDict):
       
   324                 for key, value_list in other_dict.lists():
       
   325                     self.setlistdefault(key, []).extend(value_list)
       
   326             else:
       
   327                 try:
       
   328                     for key, value in other_dict.items():
       
   329                         self.setlistdefault(key, []).append(value)
       
   330                 except TypeError:
       
   331                     raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
       
   332         for key, value in kwargs.iteritems():
       
   333             self.setlistdefault(key, []).append(value)
       
   334 
       
   335 class DotExpandedDict(dict):
       
   336     """
       
   337     A special dictionary constructor that takes a dictionary in which the keys
       
   338     may contain dots to specify inner dictionaries. It's confusing, but this
       
   339     example should make sense.
       
   340 
       
   341     >>> d = DotExpandedDict({'person.1.firstname': ['Simon'], \
       
   342             'person.1.lastname': ['Willison'], \
       
   343             'person.2.firstname': ['Adrian'], \
       
   344             'person.2.lastname': ['Holovaty']})
       
   345     >>> d
       
   346     {'person': {'1': {'lastname': ['Willison'], 'firstname': ['Simon']}, '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}}
       
   347     >>> d['person']
       
   348     {'1': {'lastname': ['Willison'], 'firstname': ['Simon']}, '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}
       
   349     >>> d['person']['1']
       
   350     {'lastname': ['Willison'], 'firstname': ['Simon']}
       
   351 
       
   352     # Gotcha: Results are unpredictable if the dots are "uneven":
       
   353     >>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1})
       
   354     {'c': 1}
       
   355     """
       
   356     def __init__(self, key_to_list_mapping):
       
   357         for k, v in key_to_list_mapping.items():
       
   358             current = self
       
   359             bits = k.split('.')
       
   360             for bit in bits[:-1]:
       
   361                 current = current.setdefault(bit, {})
       
   362             # Now assign value to current position
       
   363             try:
       
   364                 current[bits[-1]] = v
       
   365             except TypeError: # Special-case if current isn't a dict.
       
   366                 current = {bits[-1]: v}
       
   367 
       
   368 class ImmutableList(tuple):
       
   369     """
       
   370     A tuple-like object that raises useful errors when it is asked to mutate.
       
   371 
       
   372     Example::
       
   373 
       
   374         >>> a = ImmutableList(range(5), warning="You cannot mutate this.")
       
   375         >>> a[3] = '4'
       
   376         Traceback (most recent call last):
       
   377             ...
       
   378         AttributeError: You cannot mutate this.
       
   379     """
       
   380 
       
   381     def __new__(cls, *args, **kwargs):
       
   382         if 'warning' in kwargs:
       
   383             warning = kwargs['warning']
       
   384             del kwargs['warning']
       
   385         else:
       
   386             warning = 'ImmutableList object is immutable.'
       
   387         self = tuple.__new__(cls, *args, **kwargs)
       
   388         self.warning = warning
       
   389         return self
       
   390 
       
   391     def complain(self, *wargs, **kwargs):
       
   392         if isinstance(self.warning, Exception):
       
   393             raise self.warning
       
   394         else:
       
   395             raise AttributeError, self.warning
       
   396 
       
   397     # All list mutation functions complain.
       
   398     __delitem__  = complain
       
   399     __delslice__ = complain
       
   400     __iadd__     = complain
       
   401     __imul__     = complain
       
   402     __setitem__  = complain
       
   403     __setslice__ = complain
       
   404     append       = complain
       
   405     extend       = complain
       
   406     insert       = complain
       
   407     pop          = complain
       
   408     remove       = complain
       
   409     sort         = complain
       
   410     reverse      = complain
       
   411 
       
   412 class DictWrapper(dict):
       
   413     """
       
   414     Wraps accesses to a dictionary so that certain values (those starting with
       
   415     the specified prefix) are passed through a function before being returned.
       
   416     The prefix is removed before looking up the real value.
       
   417 
       
   418     Used by the SQL construction code to ensure that values are correctly
       
   419     quoted before being used.
       
   420     """
       
   421     def __init__(self, data, func, prefix):
       
   422         super(DictWrapper, self).__init__(data)
       
   423         self.func = func
       
   424         self.prefix = prefix
       
   425 
       
   426     def __getitem__(self, key):
       
   427         """
       
   428         Retrieves the real value after stripping the prefix string (if
       
   429         present). If the prefix is present, pass the value through self.func
       
   430         before returning, otherwise return the raw value.
       
   431         """
       
   432         if key.startswith(self.prefix):
       
   433             use_func = True
       
   434             key = key[len(self.prefix):]
       
   435         else:
       
   436             use_func = False
       
   437         value = super(DictWrapper, self).__getitem__(key)
       
   438         if use_func:
       
   439             return self.func(value)
       
   440         return value
       
   441