| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 | from __future__ import annotationsclass IfRange:    """Very simple object that represents the `If-Range` header in parsed    form.  It will either have neither a etag or date or one of either but    never both.    .. versionadded:: 0.7    """    def __init__(self, etag=None, date=None):        #: The etag parsed and unquoted.  Ranges always operate on strong        #: etags so the weakness information is not necessary.        self.etag = etag        #: The date in parsed format or `None`.        self.date = date    def to_header(self):        """Converts the object back into an HTTP header."""        if self.date is not None:            return http.http_date(self.date)        if self.etag is not None:            return http.quote_etag(self.etag)        return ""    def __str__(self):        return self.to_header()    def __repr__(self):        return f"<{type(self).__name__} {str(self)!r}>"class Range:    """Represents a ``Range`` header. All methods only support only    bytes as the unit. Stores a list of ranges if given, but the methods    only work if only one range is provided.    :raise ValueError: If the ranges provided are invalid.    .. versionchanged:: 0.15        The ranges passed in are validated.    .. versionadded:: 0.7    """    def __init__(self, units, ranges):        #: The units of this range.  Usually "bytes".        self.units = units        #: A list of ``(begin, end)`` tuples for the range header provided.        #: The ranges are non-inclusive.        self.ranges = ranges        for start, end in ranges:            if start is None or (end is not None and (start < 0 or start >= end)):                raise ValueError(f"{(start, end)} is not a valid range.")    def range_for_length(self, length):        """If the range is for bytes, the length is not None and there is        exactly one range and it is satisfiable it returns a ``(start, stop)``        tuple, otherwise `None`.        """        if self.units != "bytes" or length is None or len(self.ranges) != 1:            return None        start, end = self.ranges[0]        if end is None:            end = length            if start < 0:                start += length        if http.is_byte_range_valid(start, end, length):            return start, min(end, length)        return None    def make_content_range(self, length):        """Creates a :class:`~werkzeug.datastructures.ContentRange` object        from the current range and given content length.        """        rng = self.range_for_length(length)        if rng is not None:            return ContentRange(self.units, rng[0], rng[1], length)        return None    def to_header(self):        """Converts the object back into an HTTP header."""        ranges = []        for begin, end in self.ranges:            if end is None:                ranges.append(f"{begin}-" if begin >= 0 else str(begin))            else:                ranges.append(f"{begin}-{end - 1}")        return f"{self.units}={','.join(ranges)}"    def to_content_range_header(self, length):        """Converts the object into `Content-Range` HTTP header,        based on given length        """        range = self.range_for_length(length)        if range is not None:            return f"{self.units} {range[0]}-{range[1] - 1}/{length}"        return None    def __str__(self):        return self.to_header()    def __repr__(self):        return f"<{type(self).__name__} {str(self)!r}>"def _callback_property(name):    def fget(self):        return getattr(self, name)    def fset(self, value):        setattr(self, name, value)        if self.on_update is not None:            self.on_update(self)    return property(fget, fset)class ContentRange:    """Represents the content range header.    .. versionadded:: 0.7    """    def __init__(self, units, start, stop, length=None, on_update=None):        assert http.is_byte_range_valid(start, stop, length), "Bad range provided"        self.on_update = on_update        self.set(start, stop, length, units)    #: The units to use, usually "bytes"    units = _callback_property("_units")    #: The start point of the range or `None`.    start = _callback_property("_start")    #: The stop point of the range (non-inclusive) or `None`.  Can only be    #: `None` if also start is `None`.    stop = _callback_property("_stop")    #: The length of the range or `None`.    length = _callback_property("_length")    def set(self, start, stop, length=None, units="bytes"):        """Simple method to update the ranges."""        assert http.is_byte_range_valid(start, stop, length), "Bad range provided"        self._units = units        self._start = start        self._stop = stop        self._length = length        if self.on_update is not None:            self.on_update(self)    def unset(self):        """Sets the units to `None` which indicates that the header should        no longer be used.        """        self.set(None, None, units=None)    def to_header(self):        if self.units is None:            return ""        if self.length is None:            length = "*"        else:            length = self.length        if self.start is None:            return f"{self.units} */{length}"        return f"{self.units} {self.start}-{self.stop - 1}/{length}"    def __bool__(self):        return self.units is not None    def __str__(self):        return self.to_header()    def __repr__(self):        return f"<{type(self).__name__} {str(self)!r}>"# circular dependenciesfrom .. import http
 |