Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Does parsing of ETag-related headers: If-None-Matches, If-Matches 

3 

4Also If-Range parsing 

5""" 

6 

7from webob.datetime_utils import ( 

8 parse_date, 

9 serialize_date, 

10 ) 

11from webob.descriptors import _rx_etag 

12from webob.util import header_docstring 

13 

14__all__ = ['AnyETag', 'NoETag', 'ETagMatcher', 'IfRange', 'etag_property'] 

15 

16 

17def etag_property(key, default, rfc_section, strong=True): 

18 doc = header_docstring(key, rfc_section) 

19 doc += " Converts it as a Etag." 

20 

21 def fget(req): 

22 value = req.environ.get(key) 

23 if not value: 

24 return default 

25 else: 

26 return ETagMatcher.parse(value, strong=strong) 

27 

28 def fset(req, val): 

29 if val is None: 

30 req.environ[key] = None 

31 else: 

32 req.environ[key] = str(val) 

33 

34 def fdel(req): 

35 del req.environ[key] 

36 

37 return property(fget, fset, fdel, doc=doc) 

38 

39 

40class _AnyETag(object): 

41 """ 

42 Represents an ETag of *, or a missing ETag when matching is 'safe' 

43 """ 

44 

45 def __repr__(self): 

46 return '<ETag *>' 

47 

48 def __nonzero__(self): 

49 return False 

50 

51 __bool__ = __nonzero__ # python 3 

52 

53 def __contains__(self, other): 

54 return True 

55 

56 def __str__(self): 

57 return '*' 

58 

59 

60AnyETag = _AnyETag() 

61 

62 

63class _NoETag(object): 

64 """ 

65 Represents a missing ETag when matching is unsafe 

66 """ 

67 

68 def __repr__(self): 

69 return '<No ETag>' 

70 

71 def __nonzero__(self): 

72 return False 

73 

74 __bool__ = __nonzero__ # python 3 

75 

76 def __contains__(self, other): 

77 return False 

78 

79 def __str__(self): 

80 return '' 

81 

82 

83NoETag = _NoETag() 

84 

85 

86# TODO: convert into a simple tuple 

87 

88class ETagMatcher(object): 

89 def __init__(self, etags): 

90 self.etags = etags 

91 

92 def __contains__(self, other): 

93 return other in self.etags 

94 

95 def __repr__(self): 

96 return '<ETag %s>' % (' or '.join(self.etags)) 

97 

98 @classmethod 

99 def parse(cls, value, strong=True): 

100 """ 

101 Parse this from a header value 

102 """ 

103 if value == '*': 

104 return AnyETag 

105 if not value: 

106 return cls([]) 

107 matches = _rx_etag.findall(value) 

108 if not matches: 

109 return cls([value]) 

110 elif strong: 

111 return cls([t for w, t in matches if not w]) 

112 else: 

113 return cls([t for w, t in matches]) 

114 

115 def __str__(self): 

116 return ', '.join(map('"%s"'.__mod__, self.etags)) 

117 

118 

119class IfRange(object): 

120 def __init__(self, etag): 

121 self.etag = etag 

122 

123 @classmethod 

124 def parse(cls, value): 

125 """ 

126 Parse this from a header value. 

127 """ 

128 if not value: 

129 return cls(AnyETag) 

130 elif value.endswith(' GMT'): 

131 # Must be a date 

132 return IfRangeDate(parse_date(value)) 

133 else: 

134 return cls(ETagMatcher.parse(value)) 

135 

136 def __contains__(self, resp): 

137 """ 

138 Return True if the If-Range header matches the given etag or last_modified 

139 """ 

140 return resp.etag_strong in self.etag 

141 

142 def __nonzero__(self): 

143 return bool(self.etag) 

144 

145 def __repr__(self): 

146 return '%s(%r)' % ( 

147 self.__class__.__name__, 

148 self.etag 

149 ) 

150 

151 def __str__(self): 

152 return str(self.etag) if self.etag else '' 

153 

154 __bool__ = __nonzero__ # python 3 

155 

156 

157class IfRangeDate(object): 

158 def __init__(self, date): 

159 self.date = date 

160 

161 def __contains__(self, resp): 

162 last_modified = resp.last_modified 

163 return last_modified and (last_modified <= self.date) 

164 

165 def __repr__(self): 

166 return '%s(%r)' % ( 

167 self.__class__.__name__, 

168 self.date 

169 ) 

170 

171 def __str__(self): 

172 return serialize_date(self.date)