Skip to content

Commit

Permalink
Merge http-headers-py3-6080
Browse files Browse the repository at this point in the history
Author: exarkun
Reviewer: itamarst
Fixes: twisted#6080

Port `twisted.web.http_headers` to Python 3.  Also fix some structural
documentation issues.


git-svn-id: svn://svn.twistedmatrix.com/svn/Twisted/trunk@36042 bbbe8e31-12d6-0310-92fd-ac37d47ddeeb
  • Loading branch information
exarkun committed Oct 9, 2012
1 parent 9d59543 commit 28c9c6c
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 213 deletions.
4 changes: 4 additions & 0 deletions admin/_twistedpython3.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@
"twisted.trial.unittest",
"twisted.trial.util",
"twisted._version",
"twisted.web",
"twisted.web.http_headers",
"twisted.web.test",
]


Expand Down Expand Up @@ -140,4 +143,5 @@
"twisted.trial.test.test_tests",
"twisted.trial.test.test_util",
"twisted.trial.test.test_warning",
"twisted.web.test.test_http_headers",
]
120 changes: 52 additions & 68 deletions twisted/web/http_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,33 @@
An API for storing HTTP header names and values.
"""

from __future__ import division, absolute_import

from UserDict import DictMixin
from collections import MutableMapping

from twisted.python.compat import comparable, cmp


def _dashCapitalize(name):
"""
Return a string which is capitalized using '-' as a word separator.
Return a byte string which is capitalized using '-' as a word separator.
@param name: The name of the header to capitalize.
@type name: str
@type name: C{bytes}
@return: The given header capitalized using '-' as a word separator.
@rtype: str
@rtype: C{bytes}
"""
return '-'.join([word.capitalize() for word in name.split('-')])
return b'-'.join([word.capitalize() for word in name.split(b'-')])



class _DictHeaders(DictMixin):
class _DictHeaders(MutableMapping):
"""
A C{dict}-like wrapper around L{Headers} to provide backwards compatibility
for L{Request.received_headers} and L{Request.headers} which used to be
plain C{dict} instances.
for L{twisted.web.http.Request.received_headers} and
L{twisted.web.http.Request.headers} which used to be plain C{dict}
instances.
@type _headers: L{Headers}
@ivar _headers: The real header storage object.
Expand Down Expand Up @@ -63,63 +67,41 @@ def __delitem__(self, key):
raise KeyError(key)


def keys(self):
"""
Return a list of all header names.
"""
return [k.lower() for k, v in self._headers.getAllRawHeaders()]


def copy(self):
"""
Return a C{dict} mapping each header name to the last corresponding
header value.
"""
return dict(self.items())


# Python 2.3 DictMixin.setdefault is defined so as not to have a default
# for the value parameter. This is necessary to make this setdefault look
# like dict.setdefault on Python 2.3. -exarkun
def setdefault(self, name, value=None):
def __iter__(self):
"""
Retrieve the last value for the given header name. If there are no
values present for that header, set the value to C{value} and return
that instead. Note that C{None} is the default for C{value} for
backwards compatibility, but header values may only be of type C{str}.
Return an iterator of the lowercase name of each header present.
"""
return DictMixin.setdefault(self, name, value)
for k, v in self._headers.getAllRawHeaders():
yield k.lower()


# The remaining methods are only for efficiency. The same behavior
# should remain even if they are removed. For details, see
# <http://docs.python.org/lib/module-UserDict.html>.
# -exarkun
def __contains__(self, name):
def __len__(self):
"""
Return C{True} if the named header is present, C{False} otherwise.
Return the number of distinct headers present.
"""
return self._headers.getRawHeaders(name) is not None
# XXX Too many _
return len(self._headers._rawHeaders)


def __iter__(self):
# Extra methods that MutableMapping doesn't care about but that we do.
def copy(self):
"""
Return an iterator of the lowercase name of each header present.
Return a C{dict} mapping each header name to the last corresponding
header value.
"""
for k, v in self._headers.getAllRawHeaders():
yield k.lower()
return dict(self.items())


def iteritems(self):
def has_key(self, key):
"""
Return an iterable of two-tuples of each lower-case header name and the
last value for that header.
Return C{True} if C{key} is a header in this collection, C{False}
otherwise.
"""
for k, v in self._headers.getAllRawHeaders():
yield k.lower(), v[-1]
return key in self



@comparable
class Headers(object):
"""
This class stores the HTTP headers as both a parsed representation
Expand All @@ -129,22 +111,22 @@ class Headers(object):
@cvar _caseMappings: A C{dict} that maps lowercase header names
to their canonicalized representation.
@ivar _rawHeaders: A C{dict} mapping header names as C{str} to C{lists} of
header values as C{str}.
@ivar _rawHeaders: A C{dict} mapping header names as C{bytes} to C{lists} of
header values as C{bytes}.
"""
_caseMappings = {
'content-md5': 'Content-MD5',
'dnt': 'DNT',
'etag': 'ETag',
'p3p': 'P3P',
'te': 'TE',
'www-authenticate': 'WWW-Authenticate',
'x-xss-protection': 'X-XSS-Protection'}
b'content-md5': b'Content-MD5',
b'dnt': b'DNT',
b'etag': b'ETag',
b'p3p': b'P3P',
b'te': b'TE',
b'www-authenticate': b'WWW-Authenticate',
b'x-xss-protection': b'X-XSS-Protection'}

def __init__(self, rawHeaders=None):
self._rawHeaders = {}
if rawHeaders is not None:
for name, values in rawHeaders.iteritems():
for name, values in rawHeaders.items():
self.setRawHeaders(name, values[:])


Expand All @@ -161,7 +143,9 @@ def __cmp__(self, other):
the same raw headers.
"""
if isinstance(other, Headers):
return cmp(self._rawHeaders, other._rawHeaders)
return cmp(
sorted(self._rawHeaders.items()),
sorted(other._rawHeaders.items()))
return NotImplemented


Expand All @@ -176,7 +160,7 @@ def hasHeader(self, name):
"""
Check for the existence of a given header.
@type name: C{str}
@type name: C{bytes}
@param name: The name of the HTTP header to check for.
@rtype: C{bool}
Expand All @@ -189,7 +173,7 @@ def removeHeader(self, name):
"""
Remove the named header from this header object.
@type name: C{str}
@type name: C{bytes}
@param name: The name of the HTTP header to remove.
@return: C{None}
Expand All @@ -201,7 +185,7 @@ def setRawHeaders(self, name, values):
"""
Sets the raw representation of the given header.
@type name: C{str}
@type name: C{bytes}
@param name: The name of the HTTP header to set the values for.
@type values: C{list}
Expand All @@ -220,10 +204,10 @@ def addRawHeader(self, name, value):
"""
Add a new raw value for the given header.
@type name: C{str}
@type name: C{bytes}
@param name: The name of the header for which to set the value.
@type value: C{str}
@type value: C{bytes}
@param value: The value to set for the named header.
"""
values = self.getRawHeaders(name)
Expand All @@ -238,7 +222,7 @@ def getRawHeaders(self, name, default=None):
Returns a list of headers matching the given name as the raw string
given.
@type name: C{str}
@type name: C{bytes}
@param name: The name of the HTTP header to get the values of.
@param default: The value to return if no header with the given C{name}
Expand All @@ -256,19 +240,19 @@ def getAllRawHeaders(self):
object, as strings. The keys are capitalized in canonical
capitalization.
"""
for k, v in self._rawHeaders.iteritems():
for k, v in self._rawHeaders.items():
yield self._canonicalNameCaps(k), v


def _canonicalNameCaps(self, name):
"""
Return the canonical name for the given header.
@type name: C{str}
@type name: C{bytes}
@param name: The all-lowercase header name to capitalize in its
canonical form.
@rtype: C{str}
@rtype: C{bytes}
@return: The canonical name of the header.
"""
return self._caseMappings.get(name, _dashCapitalize(name))
Expand Down
Loading

0 comments on commit 28c9c6c

Please sign in to comment.