From 5172d24e66d748459963bc6a344a68023e44e183 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 14 May 2011 21:50:25 -0400 Subject: [PATCH] New CaseInsensitiveDict --- requests/structures.py | 368 ++++------------------------------------- 1 file changed, 36 insertions(+), 332 deletions(-) diff --git a/requests/structures.py b/requests/structures.py index 96b4ea4..4092f7a 100644 --- a/requests/structures.py +++ b/requests/structures.py @@ -1,360 +1,64 @@ +# -*- coding: utf-8 -*- -# from: werkzeug +""" +requests.structures +~~~~~~~~~~~~~~~~~~~ -class TypeConversionDict(dict): - """Works like a regular dict but the :meth:`get` method can perform - type conversions. :class:`MultiDict` and :class:`CombinedMultiDict` - are subclasses of this class and provide the same feature. - """ +Datastructures that power Requests. - def get(self, key, default=None, type=None): - """Return the default value if the requested data doesn't exist. - If `type` is provided and is a callable it should convert the value, - return it or raise a :exc:`ValueError` if that is not possible. In - this case the function will return the default as if the value was not - found: +""" - >>> d = TypeConversionDict(foo='42', bar='blub') - >>> d.get('foo', type=int) - 42 - >>> d.get('bar', -1, type=int) - -1 +from UserDict import DictMixin - :param key: The key to be looked up. - :param default: The default value to be returned if the key can't - be looked up. If not further specified `None` is - returned. - :param type: A callable that is used to cast the value in the - :class:`MultiDict`. If a :exc:`ValueError` is raised - by this callable the default value is returned. - """ - try: - rv = self[key] - if type is not None: - rv = type(rv) - except (KeyError, ValueError): - rv = default - return rv +class CaseInsensitiveDict(DictMixin): + """docstring for CaseInsensitiveDict""" + def __init__(self): + # super(CaseInsensitiveDict, self).__init__() + self.data = dict() -# from: werkzeug - -class MultiDict(TypeConversionDict): - """A :class:`MultiDict` is a dictionary subclass customized to deal with - multiple values for the same key which is for example used by the parsing - functions in the wrappers. This is necessary because some HTML form - elements pass multiple values for the same key. - - :class:`MultiDict` implements all standard dictionary methods. - Internally, it saves all values for a key as a list, but the standard dict - access methods will only return the first value for a key. If you want to - gain access to the other values, too, you have to use the `list` methods as - explained below. - - Basic Usage: - - >>> d = MultiDict([('a', 'b'), ('a', 'c')]) - >>> d - MultiDict([('a', 'b'), ('a', 'c')]) - >>> d['a'] - 'b' - >>> d.getlist('a') - ['b', 'c'] - >>> 'a' in d - True - - It behaves like a normal dict thus all dict functions will only return the - first value when multiple values for one key are found. - - From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a - subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will - render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP - exceptions. - - A :class:`MultiDict` can be constructed from an iterable of - ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2 - onwards some keyword parameters. - - :param mapping: the initial value for the :class:`MultiDict`. Either a - regular dict, an iterable of ``(key, value)`` tuples - or `None`. - """ - - # the key error this class raises. Because of circular dependencies - # with the http exception module this class is created at the end of - # this module. - KeyError = None - - def __init__(self, mapping=None): - if isinstance(mapping, MultiDict): - dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists())) - elif isinstance(mapping, dict): - tmp = {} - for key, value in mapping.iteritems(): - if isinstance(value, (tuple, list)): - value = list(value) - else: - value = [value] - tmp[key] = value - dict.__init__(self, tmp) - else: - tmp = {} - for key, value in mapping or (): - tmp.setdefault(key, []).append(value) - dict.__init__(self, tmp) + def __repr__(self): + return self.data.__repr__() def __getstate__(self): - return dict(self.lists()) - - def __setstate__(self, value): - dict.clear(self) - dict.update(self, value) - - def __iter__(self): - return self.iterkeys() - - def __getitem__(self, key): - """Return the first data value for this key; - raises KeyError if not found. - - :param key: The key to be looked up. - :raise KeyError: if the key does not exist. - """ - if key in self: - return dict.__getitem__(self, key)[0] - raise self.KeyError(key) - - def __setitem__(self, key, value): - """Like :meth:`add` but removes an existing key first. - - :param key: the key for the value. - :param value: the value to set. - """ - dict.__setitem__(self, key, [value]) - - def add(self, key, value): - """Adds a new value for the key. - - .. versionadded:: 0.6 - - :param key: the key for the value. - :param value: the value to add. - """ - dict.setdefault(self, key, []).append(value) + return self.data.copy() - def getlist(self, key, type=None): - """Return the list of items for a given key. If that key is not in the - `MultiDict`, the return value will be an empty list. Just as `get` - `getlist` accepts a `type` parameter. All items will be converted - with the callable defined there. + def __setstate__(self, d): + self.data = d - :param key: The key to be looked up. - :param type: A callable that is used to cast the value in the - :class:`MultiDict`. If a :exc:`ValueError` is raised - by this callable the value will be removed from the list. - :return: a :class:`list` of all the values for the key. - """ - try: - rv = dict.__getitem__(self, key) - except KeyError: - return [] - if type is None: - return list(rv) - result = [] - for item in rv: - try: - result.append(type(item)) - except ValueError: - pass - return result + @property + def _lower_keys(self): + return map(str.lower, self.data.keys()) - def setlist(self, key, new_list): - """Remove the old values for a key and add new ones. Note that the list - you pass the values in will be shallow-copied before it is inserted in - the dictionary. - >>> d = MultiDict() - >>> d.setlist('foo', ['1', '2']) - >>> d['foo'] - '1' - >>> d.getlist('foo') - ['1', '2'] + def __contains__(self, key): + return key.lower() in self._lower_keys - :param key: The key for which the values are set. - :param new_list: An iterable with the new values for the key. Old values - are removed first. - """ - dict.__setitem__(self, key, list(new_list)) - def setdefault(self, key, default=None): - """Returns the value for the key if it is in the dict, otherwise it - returns `default` and sets that value for `key`. - - :param key: The key to be looked up. - :param default: The default value to be returned if the key is not - in the dict. If not further specified it's `None`. - """ - if key not in self: - self[key] = default - else: - default = self[key] - return default - - def setlistdefault(self, key, default_list=None): - """Like `setdefault` but sets multiple values. The list returned - is not a copy, but the list that is actually used internally. This - means that you can put new values into the dict by appending items - to the list: - - >>> d = MultiDict({"foo": 1}) - >>> d.setlistdefault("foo").extend([2, 3]) - >>> d.getlist("foo") - [1, 2, 3] - - :param key: The key to be looked up. - :param default: An iterable of default values. It is either copied - (in case it was a list) or converted into a list - before returned. - :return: a :class:`list` - """ - if key not in self: - default_list = list(default_list or ()) - dict.__setitem__(self, key, default_list) - else: - default_list = dict.__getitem__(self, key) - return default_list - - def items(self, multi=False): - """Return a list of ``(key, value)`` pairs. - - :param multi: If set to `True` the list returned will have a - pair for each value of each key. Otherwise it - will only contain pairs for the first value of - each key. - - :return: a :class:`list` - """ - return list(self.iteritems(multi)) - - def lists(self): - """Return a list of ``(key, values)`` pairs, where values is the list of - all values associated with the key. - - :return: a :class:`list` - """ - return list(self.iterlists()) - - def values(self): - """Returns a list of the first value on every key's value list. - - :return: a :class:`list`. - """ - return [self[key] for key in self.iterkeys()] - - def listvalues(self): - """Return a list of all values associated with a key. Zipping - :meth:`keys` and this is the same as calling :meth:`lists`: - - >>> d = MultiDict({"foo": [1, 2, 3]}) - >>> zip(d.keys(), d.listvalues()) == d.lists() - True - - :return: a :class:`list` - """ - return list(self.iterlistvalues()) - - def iteritems(self, multi=False): - """Like :meth:`items` but returns an iterator.""" - for key, values in dict.iteritems(self): - if multi: - for value in values: - yield key, value - else: - yield key, values[0] - - def iterlists(self): - """Like :meth:`items` but returns an iterator.""" - for key, values in dict.iteritems(self): - yield key, list(values) - - def itervalues(self): - """Like :meth:`values` but returns an iterator.""" - for values in dict.itervalues(self): - yield values[0] - - def iterlistvalues(self): - """Like :meth:`listvalues` but returns an iterator.""" - return dict.itervalues(self) + def __getitem__(self, key): - def copy(self): - """Return a shallow copy of this object.""" - return self.__class__(self) + if key.lower() in self: + return self.items()[self._lower_keys.index(key.lower())][1] - def to_dict(self, flat=True): - """Return the contents as regular dict. If `flat` is `True` the - returned dict will only have the first item present, if `flat` is - `False` all values will be returned as lists. + raise KeyError - :param flat: If set to `False` the dict returned will have lists - with all the values in it. Otherwise it will only - contain the first value for each key. - :return: a :class:`dict` - """ - if flat: - return dict(self.iteritems()) - return dict(self.lists()) - def update(self, other_dict): - """update() extends rather than replaces existing key lists.""" - for key, value in iter_multi_items(other_dict): - MultiDict.add(self, key, value) + def __setitem__(self, key, value): + return self.data.__setitem__(key, value) - def pop(self, key, default=_missing): - """Pop the first item for a list on the dict. Afterwards the - key is removed from the dict, so additional values are discarded: - >>> d = MultiDict({"foo": [1, 2, 3]}) - >>> d.pop("foo") - 1 - >>> "foo" in d - False + def __delitem__(self, key): + return self.data.__delitem__(key) - :param key: the key to pop. - :param default: if provided the value to return if the key was - not in the dictionary. - """ - try: - return dict.pop(self, key)[0] - except KeyError, e: - if default is not _missing: - return default - raise self.KeyError(str(e)) - def popitem(self): - """Pop an item from the dict.""" - try: - item = dict.popitem(self) - return (item[0], item[1][0]) - except KeyError, e: - raise self.KeyError(str(e)) + def __keys__(self): + return self.data.__keys__() - def poplist(self, key): - """Pop the list for a key from the dict. If the key is not in the dict - an empty list is returned. - .. versionchanged:: 0.5 - If the key does no longer exist a list is returned instead of - raising an error. - """ - return dict.pop(self, key, []) + def __iter__(self): + return self.data.__iter__() - def popitemlist(self): - """Pop a ``(key, list)`` tuple from the dict.""" - try: - return dict.popitem(self) - except KeyError, e: - raise self.KeyError(str(e)) - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, self.items(multi=True)) + def iteritems(self): + return self.data.iteritems() -- 2.34.1