from collections import MutableMapping from webob.compat import ( iteritems_, string_types, ) from webob.multidict import MultiDict __all__ = ['ResponseHeaders', 'EnvironHeaders'] class ResponseHeaders(MultiDict): """ Dictionary view on the response headerlist. Keys are normalized for case and whitespace. """ def __getitem__(self, key): key = key.lower() for k, v in reversed(self._items): if k.lower() == key: return v raise KeyError(key) def getall(self, key): key = key.lower() return [v for (k, v) in self._items if k.lower() == key] def mixed(self): r = self.dict_of_lists() for key, val in iteritems_(r): if len(val) == 1: r[key] = val[0] return r def dict_of_lists(self): r = {} for key, val in iteritems_(self): r.setdefault(key.lower(), []).append(val) return r def __setitem__(self, key, value): norm_key = key.lower() self._items[:] = [(k, v) for (k, v) in self._items if k.lower() != norm_key] self._items.append((key, value)) def __delitem__(self, key): key = key.lower() items = self._items found = False for i in range(len(items)-1, -1, -1): if items[i][0].lower() == key: del items[i] found = True if not found: raise KeyError(key) def __contains__(self, key): key = key.lower() for k, v in self._items: if k.lower() == key: return True return False has_key = __contains__ def setdefault(self, key, default=None): c_key = key.lower() for k, v in self._items: if k.lower() == c_key: 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 %s" % repr(1 + len(args))) key = key.lower() for i in range(len(self._items)): if self._items[i][0].lower() == key: v = self._items[i][1] del self._items[i] return v if args: return args[0] else: raise KeyError(key) key2header = { 'CONTENT_TYPE': 'Content-Type', 'CONTENT_LENGTH': 'Content-Length', 'HTTP_CONTENT_TYPE': 'Content_Type', 'HTTP_CONTENT_LENGTH': 'Content_Length', } header2key = dict([(v.upper(),k) for (k,v) in key2header.items()]) def _trans_key(key): if not isinstance(key, string_types): return None elif key in key2header: return key2header[key] elif key.startswith('HTTP_'): return key[5:].replace('_', '-').title() else: return None def _trans_name(name): name = name.upper() if name in header2key: return header2key[name] return 'HTTP_'+name.replace('-', '_') class EnvironHeaders(MutableMapping): """An object that represents the headers as present in a WSGI environment. This object is a wrapper (with no internal state) for a WSGI request object, representing the CGI-style HTTP_* keys as a dictionary. Because a CGI environment can only hold one value for each key, this dictionary is single-valued (unlike outgoing headers). """ def __init__(self, environ): self.environ = environ def __getitem__(self, hname): return self.environ[_trans_name(hname)] def __setitem__(self, hname, value): self.environ[_trans_name(hname)] = value def __delitem__(self, hname): del self.environ[_trans_name(hname)] def keys(self): return filter(None, map(_trans_key, self.environ)) def __contains__(self, hname): return _trans_name(hname) in self.environ def __len__(self): return len(list(self.keys())) def __iter__(self): for k in self.keys(): yield k