4
4
Also If-Range parsing
7
from webob.datetime_utils import *
8
from webob.util import header_docstring, warn_deprecation
10
__all__ = ['AnyETag', 'NoETag', 'ETagMatcher', 'IfRange', 'NoIfRange', 'etag_property']
13
def etag_property(key, default, rfc_section):
7
from webob.datetime_utils import (
11
from webob.descriptors import _rx_etag
13
from webob.util import (
18
__all__ = ['AnyETag', 'NoETag', 'ETagMatcher', 'IfRange', 'etag_property']
20
def etag_property(key, default, rfc_section, strong=True):
14
21
doc = header_docstring(key, rfc_section)
15
22
doc += " Converts it as a Etag."
17
24
value = req.environ.get(key)
23
return ETagMatcher.parse(value)
28
return ETagMatcher.parse(value, strong=strong)
24
29
def fset(req, val):
26
31
req.environ[key] = None
68
77
def __nonzero__(self):
80
__bool__ = __nonzero__ # python 3
71
82
def __contains__(self, other):
74
def weak_match(self, other):
85
def weak_match(self, other): # pragma: no cover
75
86
_warn_weak_match_deprecated()
94
# TODO: convert into a simple tuple
83
96
class ETagMatcher(object):
85
Represents an ETag request. Supports containment to see if an
86
ETag matches. You can also use
87
``etag_matcher.weak_contains(etag)`` to allow weak ETags to match
88
(allowable for conditional GET requests, but not ranges or other
92
def __init__(self, etags, weak_etags=()):
97
def __init__(self, etags):
94
self.weak_etags = weak_etags
96
100
def __contains__(self, other):
97
return other in self.etags or other in self.weak_etags
101
return other in self.etags
99
def weak_match(self, other):
103
def weak_match(self, other): # pragma: no cover
100
104
_warn_weak_match_deprecated()
101
if other.lower().startswith('w/'):
103
return other in self.etags or other in self.weak_etags
105
106
def __repr__(self):
106
return '<ETag %s>' % (
107
' or '.join(self.etags))
107
return '<ETag %s>' % (' or '.join(self.etags))
110
def parse(cls, value):
110
def parse(cls, value, strong=True):
112
112
Parse this from a header value
117
if value.lower().startswith('w/'):
123
if value.startswith('"'):
125
etag, rest = value[1:].split('"', 1)
127
etag = value.strip(' ",')
130
rest = rest.strip(', ')
133
etag, rest = value.split(',', 1)
142
weak_results.append(etag)
146
return cls(results, weak_results)
118
matches = _rx_etag.findall(value)
122
return cls([t for w,t in matches if not w])
124
return cls([t for w,t in matches])
148
126
def __str__(self):
149
items = map('"%s"'.__mod__, self.etags)
150
for weak in self.weak_etags:
151
items.append('W/"%s"' % weak)
152
return ', '.join(items)
127
return ', '.join(map('"%s"'.__mod__, self.etags))
154
130
class IfRange(object):
156
Parses and represents the If-Range header, which can be
159
def __init__(self, etag=None, date=None):
131
def __init__(self, etag):
164
if self.etag is None:
167
etag = str(self.etag)
168
if self.date is None:
171
date = serialize_date(self.date)
172
return '<%s etag=%s, date=%s>' % (
173
self.__class__.__name__,
177
if self.etag is not None:
178
return str(self.etag)
180
return serialize_date(self.date)
184
def match(self, etag=None, last_modified=None):
186
Return True if the If-Range header matches the given etag or last_modified
188
if self.date is not None:
189
if last_modified is None:
190
# Conditional with nothing to base the condition won't work
192
return last_modified <= self.date
193
elif self.etag is not None:
196
return etag in self.etag
199
def match_response(self, response):
201
Return True if this matches the given ``webob.Response`` instance.
203
return self.match(etag=response.etag, last_modified=response.last_modified)
206
135
def parse(cls, value):
208
137
Parse this from a header value.
213
elif value and value.endswith(' GMT'):
141
elif value.endswith(' GMT'):
215
date = parse_date(value)
143
return IfRangeDate(parse_date(value))
217
etag = ETagMatcher.parse(value)
218
return cls(etag=etag, date=date)
220
class _NoIfRange(object):
222
Represents a missing If-Range header
226
return '<Empty If-Range>'
145
return cls(ETagMatcher.parse(value))
147
def __contains__(self, resp):
149
Return True if the If-Range header matches the given etag or last_modified
151
return resp.etag_strong in self.etag
231
153
def __nonzero__(self):
234
def match(self, etag=None, last_modified=None):
237
def match_response(self, response):
240
NoIfRange = _NoIfRange()
154
return bool(self.etag)
158
self.__class__.__name__,
163
return str(self.etag) if self.etag else ''
165
match = match_response = _warn_if_range_match_deprecated
167
__bool__ = __nonzero__ # python 3
169
class IfRangeDate(object):
170
def __init__(self, date):
173
def __contains__(self, resp):
174
last_modified = resp.last_modified
175
#if isinstance(last_modified, str):
176
# last_modified = parse_date(last_modified)
177
return last_modified and (last_modified <= self.date)
181
self.__class__.__name__,
183
#serialize_date(self.date)
187
return serialize_date(self.date)
189
match = match_response = _warn_if_range_match_deprecated