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