""" 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: """ Represents an ETag of *, or a missing ETag when matching is 'safe' """ def __repr__(self): return "" def __nonzero__(self): return False __bool__ = __nonzero__ # python 3 def __contains__(self, other): return True def __str__(self): return "*" AnyETag = _AnyETag() class _NoETag: """ Represents a missing ETag when matching is unsafe """ def __repr__(self): return "" 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: def __init__(self, etags): self.etags = etags def __contains__(self, other): return other in self.etags def __repr__(self): return "" % (" 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: 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 f"{self.__class__.__name__}({self.etag!r})" def __str__(self): return str(self.etag) if self.etag else "" __bool__ = __nonzero__ # python 3 class IfRangeDate: 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 f"{self.__class__.__name__}({self.date!r})" def __str__(self): return serialize_date(self.date)