summaryrefslogtreecommitdiffstats
path: root/google_appengine/lib/webob/webob/multidict.py
diff options
context:
space:
mode:
Diffstat (limited to 'google_appengine/lib/webob/webob/multidict.py')
-rwxr-xr-xgoogle_appengine/lib/webob/webob/multidict.py593
1 files changed, 593 insertions, 0 deletions
diff --git a/google_appengine/lib/webob/webob/multidict.py b/google_appengine/lib/webob/webob/multidict.py
new file mode 100755
index 0000000..e8fcb2d
--- /dev/null
+++ b/google_appengine/lib/webob/webob/multidict.py
@@ -0,0 +1,593 @@
+# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
+# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
+"""
+Gives a multi-value dictionary object (MultiDict) plus several wrappers
+"""
+import cgi
+import copy
+import sys
+from webob.util.dictmixin import DictMixin
+try:
+ reversed
+except NameError:
+ from webob.util.reversed import reversed
+
+__all__ = ['MultiDict', 'UnicodeMultiDict', 'NestedMultiDict', 'NoVars']
+
+class MultiDict(DictMixin):
+
+ """
+ An ordered dictionary that can have multiple values for each key.
+ Adds the methods getall, getone, mixed, and add to the normal
+ dictionary interface.
+ """
+
+ def __init__(self, *args, **kw):
+ if len(args) > 1:
+ raise TypeError(
+ "MultiDict can only be called with one positional argument")
+ if args:
+ if hasattr(args[0], 'iteritems'):
+ items = list(args[0].iteritems())
+ elif hasattr(args[0], 'items'):
+ items = args[0].items()
+ else:
+ items = list(args[0])
+ self._items = items
+ else:
+ self._items = []
+ self._items.extend(kw.iteritems())
+
+ #@classmethod
+ def view_list(cls, lst):
+ """
+ Create a dict that is a view on the given list
+ """
+ if not isinstance(lst, list):
+ raise TypeError(
+ "%s.view_list(obj) takes only actual list objects, not %r"
+ % (cls.__name__, lst))
+ obj = cls()
+ obj._items = lst
+ return obj
+
+ view_list = classmethod(view_list)
+
+ #@classmethod
+ def from_fieldstorage(cls, fs):
+ """
+ Create a dict from a cgi.FieldStorage instance
+ """
+ obj = cls()
+ if fs.list:
+ # fs.list can be None when there's nothing to parse
+ for field in fs.list:
+ if field.filename:
+ obj.add(field.name, field)
+ else:
+ obj.add(field.name, field.value)
+ return obj
+
+ from_fieldstorage = classmethod(from_fieldstorage)
+
+ def __getitem__(self, key):
+ for k, v in reversed(self._items):
+ if k == key:
+ return v
+ raise KeyError(key)
+
+ def __setitem__(self, key, value):
+ try:
+ del self[key]
+ except KeyError:
+ pass
+ self._items.append((key, value))
+
+ def add(self, key, value):
+ """
+ Add the key and value, not overwriting any previous value.
+ """
+ self._items.append((key, value))
+
+ def getall(self, key):
+ """
+ Return a list of all values matching the key (may be an empty list)
+ """
+ result = []
+ for k, v in self._items:
+ if key == k:
+ result.append(v)
+ return result
+
+ def getone(self, key):
+ """
+ Get one value matching the key, raising a KeyError if multiple
+ values were found.
+ """
+ v = self.getall(key)
+ if not v:
+ raise KeyError('Key not found: %r' % key)
+ if len(v) > 1:
+ raise KeyError('Multiple values match %r: %r' % (key, v))
+ return v[0]
+
+ def mixed(self):
+ """
+ Returns a dictionary where the values are either single
+ values, or a list of values when a key/value appears more than
+ once in this dictionary. This is similar to the kind of
+ dictionary often used to represent the variables in a web
+ request.
+ """
+ result = {}
+ multi = {}
+ for key, value in self.iteritems():
+ if key in result:
+ # We do this to not clobber any lists that are
+ # *actual* values in this dictionary:
+ if key in multi:
+ result[key].append(value)
+ else:
+ result[key] = [result[key], value]
+ multi[key] = None
+ else:
+ result[key] = value
+ return result
+
+ def dict_of_lists(self):
+ """
+ Returns a dictionary where each key is associated with a
+ list of values.
+ """
+ result = {}
+ for key, value in self.iteritems():
+ if key in result:
+ result[key].append(value)
+ else:
+ result[key] = [value]
+ return result
+
+ def __delitem__(self, key):
+ items = self._items
+ found = False
+ for i in range(len(items)-1, -1, -1):
+ if items[i][0] == key:
+ del items[i]
+ found = True
+ if not found:
+ raise KeyError(key)
+
+ def __contains__(self, key):
+ for k, v in self._items:
+ if k == key:
+ return True
+ return False
+
+ has_key = __contains__
+
+ def clear(self):
+ self._items = []
+
+ def copy(self):
+ return self.__class__(self)
+
+ def setdefault(self, key, default=None):
+ for k, v in self._items:
+ if key == k:
+ return v
+ self._items.append((key, default))
+ return default
+
+ def pop(self, key, *args):
+ if len(args) > 1:
+ raise TypeError, "pop expected at most 2 arguments, got "\
+ + repr(1 + len(args))
+ for i in range(len(self._items)):
+ if self._items[i][0] == key:
+ v = self._items[i][1]
+ del self._items[i]
+ return v
+ if args:
+ return args[0]
+ else:
+ raise KeyError(key)
+
+ def popitem(self):
+ return self._items.pop()
+
+ def update(self, other=None, **kwargs):
+ if other is None:
+ pass
+ elif hasattr(other, 'items'):
+ self._items.extend(other.items())
+ elif hasattr(other, 'keys'):
+ for k in other.keys():
+ self._items.append((k, other[k]))
+ else:
+ for k, v in other:
+ self._items.append((k, v))
+ if kwargs:
+ self.update(kwargs)
+
+ def __repr__(self):
+ items = ', '.join(['(%r, %r)' % v for v in self.iteritems()])
+ return '%s([%s])' % (self.__class__.__name__, items)
+
+ def __len__(self):
+ return len(self._items)
+
+ ##
+ ## All the iteration:
+ ##
+
+ def keys(self):
+ return [k for k, v in self._items]
+
+ def iterkeys(self):
+ for k, v in self._items:
+ yield k
+
+ __iter__ = iterkeys
+
+ def items(self):
+ return self._items[:]
+
+ def iteritems(self):
+ return iter(self._items)
+
+ def values(self):
+ return [v for k, v in self._items]
+
+ def itervalues(self):
+ for k, v in self._items:
+ yield v
+
+class UnicodeMultiDict(DictMixin):
+ """
+ A MultiDict wrapper that decodes returned values to unicode on the
+ fly. Decoding is not applied to assigned values.
+
+ The key/value contents are assumed to be ``str``/``strs`` or
+ ``str``/``FieldStorages`` (as is returned by the ``paste.request.parse_``
+ functions).
+
+ Can optionally also decode keys when the ``decode_keys`` argument is
+ True.
+
+ ``FieldStorage`` instances are cloned, and the clone's ``filename``
+ variable is decoded. Its ``name`` variable is decoded when ``decode_keys``
+ is enabled.
+
+ """
+ def __init__(self, multi=None, encoding=None, errors='strict',
+ decode_keys=False):
+ self.multi = multi
+ if encoding is None:
+ encoding = sys.getdefaultencoding()
+ self.encoding = encoding
+ self.errors = errors
+ self.decode_keys = decode_keys
+
+ def _decode_key(self, key):
+ if self.decode_keys:
+ try:
+ key = key.decode(self.encoding, self.errors)
+ except AttributeError:
+ pass
+ return key
+
+ def _decode_value(self, value):
+ """
+ Decode the specified value to unicode. Assumes value is a ``str`` or
+ `FieldStorage`` object.
+
+ ``FieldStorage`` objects are specially handled.
+ """
+ if isinstance(value, cgi.FieldStorage):
+ # decode FieldStorage's field name and filename
+ value = copy.copy(value)
+ if self.decode_keys:
+ value.name = value.name.decode(self.encoding, self.errors)
+ if value.filename:
+ value.filename = value.filename.decode(self.encoding,
+ self.errors)
+ else:
+ try:
+ value = value.decode(self.encoding, self.errors)
+ except AttributeError:
+ pass
+ return value
+
+ def __getitem__(self, key):
+ return self._decode_value(self.multi.__getitem__(key))
+
+ def __setitem__(self, key, value):
+ self.multi.__setitem__(key, value)
+
+ def add(self, key, value):
+ """
+ Add the key and value, not overwriting any previous value.
+ """
+ self.multi.add(key, value)
+
+ def getall(self, key):
+ """
+ Return a list of all values matching the key (may be an empty list)
+ """
+ return [self._decode_value(v) for v in self.multi.getall(key)]
+
+ def getone(self, key):
+ """
+ Get one value matching the key, raising a KeyError if multiple
+ values were found.
+ """
+ return self._decode_value(self.multi.getone(key))
+
+ def mixed(self):
+ """
+ Returns a dictionary where the values are either single
+ values, or a list of values when a key/value appears more than
+ once in this dictionary. This is similar to the kind of
+ dictionary often used to represent the variables in a web
+ request.
+ """
+ unicode_mixed = {}
+ for key, value in self.multi.mixed().iteritems():
+ if isinstance(value, list):
+ value = [self._decode_value(value) for value in value]
+ else:
+ value = self._decode_value(value)
+ unicode_mixed[self._decode_key(key)] = value
+ return unicode_mixed
+
+ def dict_of_lists(self):
+ """
+ Returns a dictionary where each key is associated with a
+ list of values.
+ """
+ unicode_dict = {}
+ for key, value in self.multi.dict_of_lists().iteritems():
+ value = [self._decode_value(value) for value in value]
+ unicode_dict[self._decode_key(key)] = value
+ return unicode_dict
+
+ def __delitem__(self, key):
+ self.multi.__delitem__(key)
+
+ def __contains__(self, key):
+ return self.multi.__contains__(key)
+
+ has_key = __contains__
+
+ def clear(self):
+ self.multi.clear()
+
+ def copy(self):
+ return UnicodeMultiDict(self.multi.copy(), self.encoding, self.errors)
+
+ def setdefault(self, key, default=None):
+ return self._decode_value(self.multi.setdefault(key, default))
+
+ def pop(self, key, *args):
+ return self._decode_value(self.multi.pop(key, *args))
+
+ def popitem(self):
+ k, v = self.multi.popitem()
+ return (self._decode_key(k), self._decode_value(v))
+
+ def __repr__(self):
+ items = ', '.join(['(%r, %r)' % v for v in self.items()])
+ return '%s([%s])' % (self.__class__.__name__, items)
+
+ def __len__(self):
+ return self.multi.__len__()
+
+ ##
+ ## All the iteration:
+ ##
+
+ def keys(self):
+ return [self._decode_key(k) for k in self.multi.iterkeys()]
+
+ def iterkeys(self):
+ for k in self.multi.iterkeys():
+ yield self._decode_key(k)
+
+ __iter__ = iterkeys
+
+ def items(self):
+ return [(self._decode_key(k), self._decode_value(v)) for \
+ k, v in self.multi.iteritems()]
+
+ def iteritems(self):
+ for k, v in self.multi.iteritems():
+ yield (self._decode_key(k), self._decode_value(v))
+
+ def values(self):
+ return [self._decode_value(v) for v in self.multi.itervalues()]
+
+ def itervalues(self):
+ for v in self.multi.itervalues():
+ yield self._decode_value(v)
+
+_dummy = object()
+
+class NestedMultiDict(MultiDict):
+ """
+ Wraps several MultiDict objects, treating it as one large MultiDict
+ """
+
+ def __init__(self, *dicts):
+ self.dicts = dicts
+
+ def __getitem__(self, key):
+ for d in self.dicts:
+ value = d.get(key, _dummy)
+ if value is not _dummy:
+ return value
+ raise KeyError(key)
+
+ def _readonly(self, *args, **kw):
+ raise KeyError("NestedMultiDict objects are read-only")
+ __setitem__ = _readonly
+ add = _readonly
+ __delitem__ = _readonly
+ clear = _readonly
+ setdefault = _readonly
+ pop = _readonly
+ popitem = _readonly
+ update = _readonly
+
+ def getall(self, key):
+ result = []
+ for d in self.dicts:
+ result.extend(d.getall(key))
+ return result
+
+ # Inherited:
+ # getone
+ # mixed
+ # dict_of_lists
+ # copy
+
+ def __contains__(self, key):
+ for d in self.dicts:
+ if key in d:
+ return True
+ return False
+
+ has_key = __contains__
+
+ def __len__(self):
+ v = 0
+ for d in self.dicts:
+ v += len(d)
+ return v
+
+ def __nonzero__(self):
+ for d in self.dicts:
+ if d:
+ return True
+ return False
+
+ def items(self):
+ return list(self.iteritems())
+
+ def iteritems(self):
+ for d in self.dicts:
+ for item in d.iteritems():
+ yield item
+
+ def values(self):
+ return list(self.itervalues())
+
+ def itervalues(self):
+ for d in self.dicts:
+ for value in d.itervalues():
+ yield value
+
+ def keys(self):
+ return list(self.iterkeys())
+
+ def __iter__(self):
+ for d in self.dicts:
+ for key in d:
+ yield key
+
+ iterkeys = __iter__
+
+class NoVars(object):
+ """
+ Represents no variables; used when no variables
+ are applicable.
+
+ This is read-only
+ """
+
+ def __init__(self, reason=None):
+ self.reason = reason or 'N/A'
+
+ def __getitem__(self, key):
+ raise KeyError("No key %r: %s" % (key, self.reason))
+
+ def __setitem__(self, *args, **kw):
+ raise KeyError("Cannot add variables: %s" % self.reason)
+
+ add = __setitem__
+ setdefault = __setitem__
+ update = __setitem__
+
+ def __delitem__(self, *args, **kw):
+ raise KeyError("No keys to delete: %s" % self.reason)
+ clear = __delitem__
+ pop = __delitem__
+ popitem = __delitem__
+
+ def get(self, key, default=None):
+ return default
+
+ def getall(self, key):
+ return []
+
+ def getone(self, key):
+ return self[key]
+
+ def mixed(self):
+ return {}
+ dict_of_lists = mixed
+
+ def __contains__(self, key):
+ return False
+ has_key = __contains__
+
+ def copy(self):
+ return self
+
+ def __repr__(self):
+ return '<%s: %s>' % (self.__class__.__name__,
+ self.reason)
+
+ def __len__(self):
+ return 0
+
+ def __cmp__(self, other):
+ return cmp({}, other)
+
+ def keys(self):
+ return []
+ def iterkeys(self):
+ return iter([])
+ __iter__ = iterkeys
+ items = keys
+ iteritems = iterkeys
+ values = keys
+ itervalues = iterkeys
+
+__test__ = {
+ 'general': """
+ >>> d = MultiDict(a=1, b=2)
+ >>> d['a']
+ 1
+ >>> d.getall('c')
+ []
+ >>> d.add('a', 2)
+ >>> d['a']
+ 2
+ >>> d.getall('a')
+ [1, 2]
+ >>> d['b'] = 4
+ >>> d.getall('b')
+ [4]
+ >>> d.keys()
+ ['a', 'a', 'b']
+ >>> d.items()
+ [('a', 1), ('a', 2), ('b', 4)]
+ >>> d.mixed()
+ {'a': [1, 2], 'b': 4}
+ >>> MultiDict([('a', 'b')], c=2)
+ MultiDict([('a', 'b'), ('c', 2)])
+ """}
+
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod()