Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/webob/cachecontrol.py : 39%

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"""
2Represents the Cache-Control header
3"""
4import re
6class UpdateDict(dict):
7 """
8 Dict that has a callback on all updates
9 """
10 # these are declared as class attributes so that
11 # we don't need to override constructor just to
12 # set some defaults
13 updated = None
14 updated_args = None
16 def _updated(self):
17 """
18 Assign to new_dict.updated to track updates
19 """
20 updated = self.updated
21 if updated is not None:
22 args = self.updated_args
23 if args is None:
24 args = (self,)
25 updated(*args)
27 def __setitem__(self, key, item):
28 dict.__setitem__(self, key, item)
29 self._updated()
31 def __delitem__(self, key):
32 dict.__delitem__(self, key)
33 self._updated()
35 def clear(self):
36 dict.clear(self)
37 self._updated()
39 def update(self, *args, **kw):
40 dict.update(self, *args, **kw)
41 self._updated()
43 def setdefault(self, key, value=None):
44 val = dict.setdefault(self, key, value)
45 if val is value:
46 self._updated()
47 return val
49 def pop(self, *args):
50 v = dict.pop(self, *args)
51 self._updated()
52 return v
54 def popitem(self):
55 v = dict.popitem(self)
56 self._updated()
57 return v
60token_re = re.compile(
61 r'([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?')
62need_quote_re = re.compile(r'[^a-zA-Z0-9._-]')
65class exists_property(object):
66 """
67 Represents a property that either is listed in the Cache-Control
68 header, or is not listed (has no value)
69 """
70 def __init__(self, prop, type=None):
71 self.prop = prop
72 self.type = type
74 def __get__(self, obj, type=None):
75 if obj is None:
76 return self
77 return self.prop in obj.properties
79 def __set__(self, obj, value):
80 if (self.type is not None
81 and self.type != obj.type):
82 raise AttributeError(
83 "The property %s only applies to %s Cache-Control" % (
84 self.prop, self.type))
86 if value:
87 obj.properties[self.prop] = None
88 else:
89 if self.prop in obj.properties:
90 del obj.properties[self.prop]
92 def __delete__(self, obj):
93 self.__set__(obj, False)
96class value_property(object):
97 """
98 Represents a property that has a value in the Cache-Control header.
100 When no value is actually given, the value of self.none is returned.
101 """
102 def __init__(self, prop, default=None, none=None, type=None):
103 self.prop = prop
104 self.default = default
105 self.none = none
106 self.type = type
108 def __get__(self, obj, type=None):
109 if obj is None:
110 return self
111 if self.prop in obj.properties:
112 value = obj.properties[self.prop]
113 if value is None:
114 return self.none
115 else:
116 return value
117 else:
118 return self.default
120 def __set__(self, obj, value):
121 if (self.type is not None
122 and self.type != obj.type):
123 raise AttributeError(
124 "The property %s only applies to %s Cache-Control" % (
125 self.prop, self.type))
126 if value == self.default:
127 if self.prop in obj.properties:
128 del obj.properties[self.prop]
129 elif value is True:
130 obj.properties[self.prop] = None # Empty value, but present
131 else:
132 obj.properties[self.prop] = value
134 def __delete__(self, obj):
135 if self.prop in obj.properties:
136 del obj.properties[self.prop]
139class CacheControl(object):
141 """
142 Represents the Cache-Control header.
144 By giving a type of ``'request'`` or ``'response'`` you can
145 control what attributes are allowed (some Cache-Control values
146 only apply to requests or responses).
147 """
149 update_dict = UpdateDict
151 def __init__(self, properties, type):
152 self.properties = properties
153 self.type = type
155 @classmethod
156 def parse(cls, header, updates_to=None, type=None):
157 """
158 Parse the header, returning a CacheControl object.
160 The object is bound to the request or response object
161 ``updates_to``, if that is given.
162 """
163 if updates_to:
164 props = cls.update_dict()
165 props.updated = updates_to
166 else:
167 props = {}
168 for match in token_re.finditer(header):
169 name = match.group(1)
170 value = match.group(2) or match.group(3) or None
171 if value:
172 try:
173 value = int(value)
174 except ValueError:
175 pass
176 props[name] = value
177 obj = cls(props, type=type)
178 if updates_to:
179 props.updated_args = (obj,)
180 return obj
182 def __repr__(self):
183 return '<CacheControl %r>' % str(self)
185 # Request values:
186 # no-cache shared (below)
187 # no-store shared (below)
188 # max-age shared (below)
189 max_stale = value_property('max-stale', none='*', type='request')
190 min_fresh = value_property('min-fresh', type='request')
191 # no-transform shared (below)
192 only_if_cached = exists_property('only-if-cached', type='request')
194 # Response values:
195 public = exists_property('public', type='response')
196 private = value_property('private', none='*', type='response')
197 no_cache = value_property('no-cache', none='*')
198 no_store = exists_property('no-store')
199 no_transform = exists_property('no-transform')
200 must_revalidate = exists_property('must-revalidate', type='response')
201 proxy_revalidate = exists_property('proxy-revalidate', type='response')
202 max_age = value_property('max-age', none=-1)
203 s_maxage = value_property('s-maxage', type='response')
204 s_max_age = s_maxage
205 stale_while_revalidate = value_property(
206 'stale-while-revalidate', type='response')
207 stale_if_error = value_property('stale-if-error', type='response')
209 def __str__(self):
210 return serialize_cache_control(self.properties)
212 def copy(self):
213 """
214 Returns a copy of this object.
215 """
216 return self.__class__(self.properties.copy(), type=self.type)
219def serialize_cache_control(properties):
220 if isinstance(properties, CacheControl):
221 properties = properties.properties
222 parts = []
223 for name, value in sorted(properties.items()):
224 if value is None:
225 parts.append(name)
226 continue
227 value = str(value)
228 if need_quote_re.search(value):
229 value = '"%s"' % value
230 parts.append('%s=%s' % (name, value))
231 return ', '.join(parts)