Integrated my history into data
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"""Functions for generating and parsing HTTP Accept: headers for
|
||||
supporting server-directed content negotiation.
|
||||
"""
|
||||
|
||||
|
||||
def generateAcceptHeader(*elements):
|
||||
"""Generate an accept header value
|
||||
|
||||
[str or (str, float)] -> str
|
||||
"""
|
||||
parts = []
|
||||
for element in elements:
|
||||
if type(element) is str:
|
||||
qs = "1.0"
|
||||
mtype = element
|
||||
else:
|
||||
mtype, q = element
|
||||
q = float(q)
|
||||
if q > 1 or q <= 0:
|
||||
raise ValueError('Invalid preference factor: %r' % q)
|
||||
|
||||
qs = '%0.1f' % (q, )
|
||||
|
||||
parts.append((qs, mtype))
|
||||
|
||||
parts.sort()
|
||||
chunks = []
|
||||
for q, mtype in parts:
|
||||
if q == '1.0':
|
||||
chunks.append(mtype)
|
||||
else:
|
||||
chunks.append('%s; q=%s' % (mtype, q))
|
||||
|
||||
return ', '.join(chunks)
|
||||
|
||||
|
||||
def parseAcceptHeader(value):
|
||||
"""Parse an accept header, ignoring any accept-extensions
|
||||
|
||||
returns a list of tuples containing main MIME type, MIME subtype,
|
||||
and quality markdown.
|
||||
|
||||
str -> [(str, str, float)]
|
||||
"""
|
||||
chunks = [chunk.strip() for chunk in value.split(',')]
|
||||
accept = []
|
||||
for chunk in chunks:
|
||||
parts = [s.strip() for s in chunk.split(';')]
|
||||
|
||||
mtype = parts.pop(0)
|
||||
if '/' not in mtype:
|
||||
# This is not a MIME type, so ignore the bad data
|
||||
continue
|
||||
|
||||
main, sub = mtype.split('/', 1)
|
||||
|
||||
for ext in parts:
|
||||
if '=' in ext:
|
||||
k, v = ext.split('=', 1)
|
||||
if k == 'q':
|
||||
try:
|
||||
q = float(v)
|
||||
break
|
||||
except ValueError:
|
||||
# Ignore poorly formed q-values
|
||||
pass
|
||||
else:
|
||||
q = 1.0
|
||||
|
||||
accept.append((q, main, sub))
|
||||
|
||||
accept.sort()
|
||||
accept.reverse()
|
||||
return [(main, sub, q) for (q, main, sub) in accept]
|
||||
|
||||
|
||||
def matchTypes(accept_types, have_types):
|
||||
"""Given the result of parsing an Accept: header, and the
|
||||
available MIME types, return the acceptable types with their
|
||||
quality markdowns.
|
||||
|
||||
For example:
|
||||
|
||||
>>> acceptable = parseAcceptHeader('text/html, text/plain; q=0.5')
|
||||
>>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg'])
|
||||
[('text/html', 1.0), ('text/plain', 0.5)]
|
||||
|
||||
|
||||
Type signature: ([(str, str, float)], [str]) -> [(str, float)]
|
||||
"""
|
||||
if not accept_types:
|
||||
# Accept all of them
|
||||
default = 1
|
||||
else:
|
||||
default = 0
|
||||
|
||||
match_main = {}
|
||||
match_sub = {}
|
||||
for (main, sub, q) in accept_types:
|
||||
if main == '*':
|
||||
default = max(default, q)
|
||||
continue
|
||||
elif sub == '*':
|
||||
match_main[main] = max(match_main.get(main, 0), q)
|
||||
else:
|
||||
match_sub[(main, sub)] = max(match_sub.get((main, sub), 0), q)
|
||||
|
||||
accepted_list = []
|
||||
order_maintainer = 0
|
||||
for mtype in have_types:
|
||||
main, sub = mtype.split('/')
|
||||
if (main, sub) in match_sub:
|
||||
q = match_sub[(main, sub)]
|
||||
else:
|
||||
q = match_main.get(main, default)
|
||||
|
||||
if q:
|
||||
accepted_list.append((1 - q, order_maintainer, q, mtype))
|
||||
order_maintainer += 1
|
||||
|
||||
accepted_list.sort()
|
||||
return [(mtype, q) for (_, _, q, mtype) in accepted_list]
|
||||
|
||||
|
||||
def getAcceptable(accept_header, have_types):
|
||||
"""Parse the accept header and return a list of available types in
|
||||
preferred order. If a type is unacceptable, it will not be in the
|
||||
resulting list.
|
||||
|
||||
This is a convenience wrapper around matchTypes and
|
||||
parseAcceptHeader.
|
||||
|
||||
(str, [str]) -> [str]
|
||||
"""
|
||||
accepted = parseAcceptHeader(accept_header)
|
||||
preferred = matchTypes(accepted, have_types)
|
||||
return [mtype for (mtype, _) in preferred]
|
||||
Reference in New Issue
Block a user