Package openid :: Package yadis :: Module accept
[frames] | no frames]

Source Code for Module openid.yadis.accept

  1  """Functions for generating and parsing HTTP Accept: headers for 
  2  supporting server-directed content negotiation. 
  3  """ 
  4   
5 -def generateAcceptHeader(*elements):
6 """Generate an accept header value 7 8 [str or (str, float)] -> str 9 """ 10 parts = [] 11 for element in elements: 12 if type(element) is str: 13 qs = "1.0" 14 mtype = element 15 else: 16 mtype, q = element 17 q = float(q) 18 if q > 1 or q <= 0: 19 raise ValueError('Invalid preference factor: %r' % q) 20 21 qs = '%0.1f' % (q,) 22 23 parts.append((qs, mtype)) 24 25 parts.sort() 26 chunks = [] 27 for q, mtype in parts: 28 if q == '1.0': 29 chunks.append(mtype) 30 else: 31 chunks.append('%s; q=%s' % (mtype, q)) 32 33 return ', '.join(chunks)
34
35 -def parseAcceptHeader(value):
36 """Parse an accept header, ignoring any accept-extensions 37 38 returns a list of tuples containing main MIME type, MIME subtype, 39 and quality markdown. 40 41 str -> [(str, str, float)] 42 """ 43 chunks = [chunk.strip() for chunk in value.split(',')] 44 accept = [] 45 for chunk in chunks: 46 parts = [s.strip() for s in chunk.split(';')] 47 48 mtype = parts.pop(0) 49 if '/' not in mtype: 50 # This is not a MIME type, so ignore the bad data 51 continue 52 53 main, sub = mtype.split('/', 1) 54 55 for ext in parts: 56 if '=' in ext: 57 k, v = ext.split('=', 1) 58 if k == 'q': 59 try: 60 q = float(v) 61 break 62 except ValueError: 63 # Ignore poorly formed q-values 64 pass 65 else: 66 q = 1.0 67 68 accept.append((q, main, sub)) 69 70 accept.sort() 71 accept.reverse() 72 return [(main, sub, q) for (q, main, sub) in accept]
73
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 77 quality markdowns. 78 79 For example: 80 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)] 84 85 86 Type signature: ([(str, str, float)], [str]) -> [(str, float)] 87 """ 88 if not accept_types: 89 # Accept all of them 90 default = 1 91 else: 92 default = 0 93 94 match_main = {} 95 match_sub = {} 96 for (main, sub, q) in accept_types: 97 if main == '*': 98 default = max(default, q) 99 continue 100 elif sub == '*': 101 match_main[main] = max(match_main.get(main, 0), q) 102 else: 103 match_sub[(main, sub)] = max(match_sub.get((main, sub), 0), q) 104 105 accepted_list = [] 106 order_maintainer = 0 107 for mtype in have_types: 108 main, sub = mtype.split('/') 109 if (main, sub) in match_sub: 110 q = match_sub[(main, sub)] 111 else: 112 q = match_main.get(main, default) 113 114 if q: 115 accepted_list.append((1 - q, order_maintainer, q, mtype)) 116 order_maintainer += 1 117 118 accepted_list.sort() 119 return [(mtype, q) for (_, _, q, mtype) in accepted_list]
120
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 124 resulting list. 125 126 This is a convenience wrapper around matchTypes and 127 parseAcceptHeader. 128 129 (str, [str]) -> [str] 130 """ 131 accepted = parseAcceptHeader(accept_header) 132 preferred = matchTypes(accepted, have_types) 133 return [mtype for (mtype, _) in preferred]
134