1
"""Functions for generating and parsing HTTP Accept: headers for
2
supporting server-directed content negotiation.
5
def generateAcceptHeader(*elements):
6
"""Generate an accept header value
8
[str or (str, float)] -> str
11
for element in elements:
12
if type(element) is str:
19
raise ValueError('Invalid preference factor: %r' % q)
23
parts.append((qs, mtype))
27
for q, mtype in parts:
31
chunks.append('%s; q=%s' % (mtype, q))
33
return ', '.join(chunks)
35
def parseAcceptHeader(value):
36
"""Parse an accept header, ignoring any accept-extensions
38
returns a list of tuples containing main MIME type, MIME subtype,
41
str -> [(str, str, float)]
43
chunks = [chunk.strip() for chunk in value.split(',')]
46
parts = [s.strip() for s in chunk.split(';')]
50
# This is not a MIME type, so ignore the bad data
53
main, sub = mtype.split('/', 1)
57
k, v = ext.split('=', 1)
63
# Ignore poorly formed q-values
68
accept.append((q, main, sub))
72
return [(main, sub, q) for (q, main, sub) in accept]
74
def matchTypes(accept_types, have_types):
75
"""Given the result of parsing an Accept: header, and the
76
available MIME types, return the acceptable types with their
81
>>> acceptable = parseAcceptHeader('text/html, text/plain; q=0.5')
82
>>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg'])
83
[('text/html', 1.0), ('text/plain', 0.5)]
86
Type signature: ([(str, str, float)], [str]) -> [(str, float)]
96
for (main, sub, q) in accept_types:
98
default = max(default, q)
101
match_main[main] = max(match_main.get(main, 0), q)
103
match_sub[(main, sub)] = max(match_sub.get((main, sub), 0), q)
107
for mtype in have_types:
108
main, sub = mtype.split('/')
109
if (main, sub) in match_sub:
110
q = match_sub[(main, sub)]
112
q = match_main.get(main, default)
115
accepted_list.append((1 - q, order_maintainer, q, mtype))
116
order_maintainer += 1
119
return [(mtype, q) for (_, _, q, mtype) in accepted_list]
121
def getAcceptable(accept_header, have_types):
122
"""Parse the accept header and return a list of available types in
123
preferred order. If a type is unacceptable, it will not be in the
126
This is a convenience wrapper around matchTypes and
129
(str, [str]) -> [str]
131
accepted = parseAcceptHeader(accept_header)
132
preferred = matchTypes(accepted, have_types)
133
return [mtype for (mtype, _) in preferred]