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
]