Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/openid/yadis/accept.py: 28%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

72 statements  

1"""Functions for generating and parsing HTTP Accept: headers for 

2supporting server-directed content negotiation. 

3""" 

4 

5 

6def generateAcceptHeader(*elements): 

7 """Generate an accept header value 

8 

9 [str or (str, float)] -> str 

10 """ 

11 parts = [] 

12 for element in elements: 

13 if type(element) is str: 

14 qs = "1.0" 

15 mtype = element 

16 else: 

17 mtype, q = element 

18 q = float(q) 

19 if q > 1 or q <= 0: 

20 raise ValueError('Invalid preference factor: %r' % q) 

21 

22 qs = '%0.1f' % (q, ) 

23 

24 parts.append((qs, mtype)) 

25 

26 parts.sort() 

27 chunks = [] 

28 for q, mtype in parts: 

29 if q == '1.0': 

30 chunks.append(mtype) 

31 else: 

32 chunks.append('%s; q=%s' % (mtype, q)) 

33 

34 return ', '.join(chunks) 

35 

36 

37def parseAcceptHeader(value): 

38 """Parse an accept header, ignoring any accept-extensions 

39 

40 returns a list of tuples containing main MIME type, MIME subtype, 

41 and quality markdown. 

42 

43 str -> [(str, str, float)] 

44 """ 

45 chunks = [chunk.strip() for chunk in value.split(',')] 

46 accept = [] 

47 for chunk in chunks: 

48 parts = [s.strip() for s in chunk.split(';')] 

49 

50 mtype = parts.pop(0) 

51 if '/' not in mtype: 

52 # This is not a MIME type, so ignore the bad data 

53 continue 

54 

55 main, sub = mtype.split('/', 1) 

56 

57 for ext in parts: 

58 if '=' in ext: 

59 k, v = ext.split('=', 1) 

60 if k == 'q': 

61 try: 

62 q = float(v) 

63 break 

64 except ValueError: 

65 # Ignore poorly formed q-values 

66 pass 

67 else: 

68 q = 1.0 

69 

70 accept.append((q, main, sub)) 

71 

72 accept.sort() 

73 accept.reverse() 

74 return [(main, sub, q) for (q, main, sub) in accept] 

75 

76 

77def matchTypes(accept_types, have_types): 

78 """Given the result of parsing an Accept: header, and the 

79 available MIME types, return the acceptable types with their 

80 quality markdowns. 

81 

82 For example: 

83 

84 >>> acceptable = parseAcceptHeader('text/html, text/plain; q=0.5') 

85 >>> matchTypes(acceptable, ['text/plain', 'text/html', 'image/jpeg']) 

86 [('text/html', 1.0), ('text/plain', 0.5)] 

87 

88 

89 Type signature: ([(str, str, float)], [str]) -> [(str, float)] 

90 """ 

91 if not accept_types: 

92 # Accept all of them 

93 default = 1 

94 else: 

95 default = 0 

96 

97 match_main = {} 

98 match_sub = {} 

99 for (main, sub, q) in accept_types: 

100 if main == '*': 

101 default = max(default, q) 

102 continue 

103 elif sub == '*': 

104 match_main[main] = max(match_main.get(main, 0), q) 

105 else: 

106 match_sub[(main, sub)] = max(match_sub.get((main, sub), 0), q) 

107 

108 accepted_list = [] 

109 order_maintainer = 0 

110 for mtype in have_types: 

111 main, sub = mtype.split('/') 

112 if (main, sub) in match_sub: 

113 q = match_sub[(main, sub)] 

114 else: 

115 q = match_main.get(main, default) 

116 

117 if q: 

118 accepted_list.append((1 - q, order_maintainer, q, mtype)) 

119 order_maintainer += 1 

120 

121 accepted_list.sort() 

122 return [(mtype, q) for (_, _, q, mtype) in accepted_list] 

123 

124 

125def getAcceptable(accept_header, have_types): 

126 """Parse the accept header and return a list of available types in 

127 preferred order. If a type is unacceptable, it will not be in the 

128 resulting list. 

129 

130 This is a convenience wrapper around matchTypes and 

131 parseAcceptHeader. 

132 

133 (str, [str]) -> [str] 

134 """ 

135 accepted = parseAcceptHeader(accept_header) 

136 preferred = matchTypes(accepted, have_types) 

137 return [mtype for (mtype, _) in preferred]