summaryrefslogtreecommitdiff
path: root/django/utils/datastructures.py
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2005-07-13 01:25:57 +0000
committerAdrian Holovaty <adrian@holovaty.com>2005-07-13 01:25:57 +0000
commited114e15106192b22ebb78ef5bf5bce72b419d13 (patch)
treef7c27f035cca8d50bd69e2ecbd7497fccec4a35a /django/utils/datastructures.py
parent07ffc7d605cc96557db28a9e35da69bc0719611b (diff)
downloaddjango-ed114e15106192b22ebb78ef5bf5bce72b419d13.tar.gz
Imported Django from private SVN repository (created from r. 8825)
git-svn-id: http://code.djangoproject.com/svn/django/trunk@3 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/utils/datastructures.py')
-rw-r--r--django/utils/datastructures.py171
1 files changed, 171 insertions, 0 deletions
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
new file mode 100644
index 0000000000..c1cb6193f5
--- /dev/null
+++ b/django/utils/datastructures.py
@@ -0,0 +1,171 @@
+class MergeDict:
+ """
+ A simple class for creating new "virtual" dictionaries that actualy look
+ up values in more than one dictionary, passed in the constructor.
+ """
+ def __init__(self, *dicts):
+ self.dicts = dicts
+
+ def __getitem__(self, key):
+ for dict in self.dicts:
+ try:
+ return dict[key]
+ except KeyError:
+ pass
+ raise KeyError
+
+ def get(self, key, default):
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def getlist(self, key):
+ for dict in self.dicts:
+ try:
+ return dict.getlist(key)
+ except KeyError:
+ pass
+ raise KeyError
+
+ def items(self):
+ item_list = []
+ for dict in self.dicts:
+ item_list.extend(dict.items())
+ return item_list
+
+ def has_key(self, key):
+ for dict in self.dicts:
+ if dict.has_key(key):
+ return True
+ return False
+
+class MultiValueDictKeyError(KeyError):
+ pass
+
+class MultiValueDict:
+ """
+ A dictionary-like class customized to deal with multiple values for the same key.
+
+ >>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
+ >>> d['name']
+ 'Simon'
+ >>> d.getlist('name')
+ ['Adrian', 'Simon']
+ >>> d.get('lastname', 'nonexistent')
+ 'nonexistent'
+ >>> d.setlist('lastname', ['Holovaty', 'Willison'])
+
+ This class exists to solve the irritating problem raised by cgi.parse_qs,
+ which returns a list for every key, even though most Web forms submit
+ single name-value pairs.
+ """
+ def __init__(self, key_to_list_mapping=None):
+ self.data = key_to_list_mapping or {}
+
+ def __repr__(self):
+ return repr(self.data)
+
+ def __getitem__(self, key):
+ "Returns the data value for this key; raises KeyError if not found"
+ if self.data.has_key(key):
+ try:
+ return self.data[key][-1] # in case of duplicates, use last value ([-1])
+ except IndexError:
+ return []
+ raise MultiValueDictKeyError, "Key '%s' not found in MultiValueDict %s" % (key, self.data)
+
+ def __setitem__(self, key, value):
+ self.data[key] = [value]
+
+ def __len__(self):
+ return len(self.data)
+
+ def get(self, key, default):
+ "Returns the default value if the requested data doesn't exist"
+ try:
+ val = self[key]
+ except (KeyError, IndexError):
+ return default
+ if val == []:
+ return default
+ return val
+
+ def getlist(self, key):
+ "Returns an empty list if the requested data doesn't exist"
+ try:
+ return self.data[key]
+ except KeyError:
+ return []
+
+ def setlist(self, key, list_):
+ self.data[key] = list_
+
+ def appendlist(self, key, item):
+ "Appends an item to the internal list associated with key"
+ try:
+ self.data[key].append(item)
+ except KeyError:
+ self.data[key] = [item]
+
+ def has_key(self, key):
+ return self.data.has_key(key)
+
+ def items(self):
+ # we don't just return self.data.items() here, because we want to use
+ # self.__getitem__() to access the values as *strings*, not lists
+ return [(key, self[key]) for key in self.data.keys()]
+
+ def keys(self):
+ return self.data.keys()
+
+ def update(self, other_dict):
+ if isinstance(other_dict, MultiValueDict):
+ for key, value_list in other_dict.data.items():
+ self.data.setdefault(key, []).extend(value_list)
+ elif type(other_dict) == type({}):
+ for key, value in other_dict.items():
+ self.data.setdefault(key, []).append(value)
+ else:
+ raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
+
+ def copy(self):
+ "Returns a copy of this object"
+ import copy
+ cp = copy.deepcopy(self)
+ return cp
+
+class DotExpandedDict(dict):
+ """
+ A special dictionary constructor that takes a dictionary in which the keys
+ may contain dots to specify inner dictionaries. It's confusing, but this
+ example should make sense.
+
+ >>> d = DotExpandedDict({'person.1.firstname': ['Simon'],
+ 'person.1.lastname': ['Willison'],
+ 'person.2.firstname': ['Adrian'],
+ 'person.2.lastname': ['Holovaty']})
+ >>> d
+ {'person': {'1': {'lastname': ['Willison'], 'firstname': ['Simon']},
+ '2': {'lastname': ['Holovaty'], 'firstname': ['Adrian']}}}
+ >>> d['person']
+ {'1': {'firstname': ['Simon'], 'lastname': ['Willison'],
+ '2': {'firstname': ['Adrian'], 'lastname': ['Holovaty']}
+ >>> d['person']['1']
+ {'firstname': ['Simon'], 'lastname': ['Willison']}
+
+ # Gotcha: Results are unpredictable if the dots are "uneven":
+ >>> DotExpandedDict({'c.1': 2, 'c.2': 3, 'c': 1})
+ >>> {'c': 1}
+ """
+ def __init__(self, key_to_list_mapping):
+ for k, v in key_to_list_mapping.items():
+ current = self
+ bits = k.split('.')
+ for bit in bits[:-1]:
+ current = current.setdefault(bit, {})
+ # Now assign value to current position
+ try:
+ current[bits[-1]] = v
+ except TypeError: # Special-case if current isn't a dict.
+ current = {bits[-1]: v}