Created
May 16, 2012 21:43
-
-
Save samuraisam/2714195 to your computer and use it in GitHub Desktop.
Parse an accept header in Python w/ support for vendor types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The author disclaims copyright to this source code. In place of a legal | |
# notice, here is a blessing: | |
# | |
# May you do good and not evil. | |
# May you find forgiveness for yourself and forgive others. | |
# May you share freely, never taking more than you give. | |
# | |
# It is based on a snipped found in this project: | |
# https://github.com/martinblech/mimerender | |
def parse_accept_header(accept): | |
""" | |
Parse the Accept header *accept*, returning a list with 3-tuples of | |
[(str(media_type), dict(params), float(q_value)),] ordered by q values. | |
If the accept header includes vendor-specific types like:: | |
application/vnd.yourcompany.yourproduct-v1.1+json | |
It will actually convert the vendor and version into parameters and | |
convert the content type into `application/json` so appropriate content | |
negotiation decisions can be made. | |
Default `q` for values that are not specified is 1.0 | |
""" | |
result = [] | |
for media_range in accept.split(","): | |
parts = media_range.split(";") | |
media_type = parts.pop(0).strip() | |
media_params = [] | |
# convert vendor-specific content types into something useful (see | |
# docstring) | |
typ, subtyp = media_type.split('/') | |
# check for a + in the sub-type | |
if '+' in subtyp: | |
# if it exists, determine if the subtype is a vendor-specific type | |
vnd, sep, extra = subtyp.partition('+') | |
if vnd.startswith('vnd'): | |
# and then... if it ends in something like "-v1.1" parse the | |
# version out | |
if '-v' in vnd: | |
vnd, sep, rest = vnd.rpartition('-v') | |
if len(rest): | |
# add the version as a media param | |
try: | |
version = media_params.append(('version', | |
float(rest))) | |
except ValueError: | |
version = 1.0 # could not be parsed | |
# add the vendor code as a media param | |
media_params.append(('vendor', vnd)) | |
# and re-write media_type to something like application/json so | |
# it can be used usefully when looking up emitters | |
media_type = '{}/{}'.format(typ, extra) | |
q = 1.0 | |
for part in parts: | |
(key, value) = part.lstrip().split("=", 1) | |
key = key.strip() | |
value = value.strip() | |
if key == "q": | |
q = float(value) | |
else: | |
media_params.append((key, value)) | |
result.append((media_type, dict(media_params), q)) | |
result.sort(lambda x, y: -cmp(x[2], y[2])) | |
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from unittest import TestCase | |
from parse_accept import parse_accept_header | |
class TestParseAcceptHeader(TestCase): | |
def test_parse_accept_header_browser(self): | |
accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json" | |
should = [('text/html', {}, 1.0), | |
('application/xhtml+xml', {}, 1.0), | |
('application/json', {}, 1.0), | |
('application/xml', {}, 0.9), | |
('*/*', {}, 0.8)] | |
self.assertEquals(parse_accept_header(accept), should) | |
def test_parse_accept_header_smart_client(self): | |
accept = "application/vnd.ficture.lightt-v1.1+json" | |
should = [('application/json', {'version': 1.1, | |
'vendor': 'vnd.ficture.lightt'}, 1.0)] | |
self.assertEquals(parse_accept_header(accept), should) | |
def test_parse_accept_header_dumbish_client(self): | |
accept = "application/vnd.ficture.lightt-v1.0" | |
should = [('application/vnd.ficture.lightt-v1.0', {}, 1.0)] | |
self.assertEquals(parse_accept_header(accept), should) | |
def test_parse_accept_header_also_dumb_client(self): | |
accept = "application/vnd.ficture.lightt" | |
should = [('application/vnd.ficture.lightt', {}, 1.0)] | |
self.assertEquals(parse_accept_header(accept), should) | |
def test_parse_accept_header_dumb_client(self): | |
accept = "application/json" | |
should = [('application/json', {}, 1.0)] | |
self.assertEquals(parse_accept_header(accept), should) | |
def test_parse_accept_header_really_dumb_client(self): | |
accept = "" | |
should = [('', {}, 1.0)] | |
self.assertEquals(parse_accept_header(accept), should) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment