impuls/lib/python3.11/site-packages/webob/etag.py

173 lines
3.7 KiB
Python
Raw Normal View History

"""
Does parsing of ETag-related headers: If-None-Matches, If-Matches
Also If-Range parsing
"""
from webob.datetime_utils import (
parse_date,
serialize_date,
)
from webob.descriptors import _rx_etag
from webob.util import header_docstring
__all__ = ['AnyETag', 'NoETag', 'ETagMatcher', 'IfRange', 'etag_property']
def etag_property(key, default, rfc_section, strong=True):
doc = header_docstring(key, rfc_section)
doc += " Converts it as a Etag."
def fget(req):
value = req.environ.get(key)
if not value:
return default
else:
return ETagMatcher.parse(value, strong=strong)
def fset(req, val):
if val is None:
req.environ[key] = None
else:
req.environ[key] = str(val)
def fdel(req):
del req.environ[key]
return property(fget, fset, fdel, doc=doc)
class _AnyETag(object):
"""
Represents an ETag of *, or a missing ETag when matching is 'safe'
"""
def __repr__(self):
return '<ETag *>'
def __nonzero__(self):
return False
__bool__ = __nonzero__ # python 3
def __contains__(self, other):
return True
def __str__(self):
return '*'
AnyETag = _AnyETag()
class _NoETag(object):
"""
Represents a missing ETag when matching is unsafe
"""
def __repr__(self):
return '<No ETag>'
def __nonzero__(self):
return False
__bool__ = __nonzero__ # python 3
def __contains__(self, other):
return False
def __str__(self):
return ''
NoETag = _NoETag()
# TODO: convert into a simple tuple
class ETagMatcher(object):
def __init__(self, etags):
self.etags = etags
def __contains__(self, other):
return other in self.etags
def __repr__(self):
return '<ETag %s>' % (' or '.join(self.etags))
@classmethod
def parse(cls, value, strong=True):
"""
Parse this from a header value
"""
if value == '*':
return AnyETag
if not value:
return cls([])
matches = _rx_etag.findall(value)
if not matches:
return cls([value])
elif strong:
return cls([t for w, t in matches if not w])
else:
return cls([t for w, t in matches])
def __str__(self):
return ', '.join(map('"%s"'.__mod__, self.etags))
class IfRange(object):
def __init__(self, etag):
self.etag = etag
@classmethod
def parse(cls, value):
"""
Parse this from a header value.
"""
if not value:
return cls(AnyETag)
elif value.endswith(' GMT'):
# Must be a date
return IfRangeDate(parse_date(value))
else:
return cls(ETagMatcher.parse(value))
def __contains__(self, resp):
"""
Return True if the If-Range header matches the given etag or last_modified
"""
return resp.etag_strong in self.etag
def __nonzero__(self):
return bool(self.etag)
def __repr__(self):
return '%s(%r)' % (
self.__class__.__name__,
self.etag
)
def __str__(self):
return str(self.etag) if self.etag else ''
__bool__ = __nonzero__ # python 3
class IfRangeDate(object):
def __init__(self, date):
self.date = date
def __contains__(self, resp):
last_modified = resp.last_modified
return last_modified and (last_modified <= self.date)
def __repr__(self):
return '%s(%r)' % (
self.__class__.__name__,
self.date
)
def __str__(self):
return serialize_date(self.date)