r"""When preparing a HTTP GET request, the HTTP `range request
<https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests>`_
header must be provided as a :class:`dict`, for example:
.. code-block:: python
{"range": "bytes=0-1"}
would request the two bytes at positions ``0`` and ``1`` (i.e. the inclusive
interval ``[0,1]``).
An empty range can also be specified with the value ``bytes="-0"``, which is useful to
determine the total length of a file (as the `Content-Range
<https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range>`_ header
returned by the server contains the total size of the file from which the range was taken).
"""
from __future__ import annotations
from typing import TYPE_CHECKING
MYPY = False
if TYPE_CHECKING: # pragma: no cover
from ranges import Range
if not MYPY: # Sphinx docstring import
import range_streams
from .range_utils import range_termini
__all__ = [
"byte_range_from_range_obj",
"range_header",
"PartialContentStatusError",
"detect_header_value",
]
[docs]def byte_range_from_range_obj(rng: Range) -> str:
"""Prepare the byte range substring for a HTTP `range request
<https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests>`_.
For example:
>>> from range_streams.http_utils import byte_range_from_range_obj
>>> byte_range_from_range_obj(Range(0,2))
'0-1'
Args:
rng : range of the bytes to be requested (0-based)
Returns:
A hyphen-separated string of start and end positions. The start position
is missing if the range provided is empty, and this corresponds to a request
for "the last zero bytes" i.e. an empty range request.
"""
if rng.isempty():
byte_range = "0-"
else:
start_byte, end_byte = range_termini(rng)
byte_range = f"{start_byte}-{end_byte}"
return byte_range
[docs]class PartialContentStatusError(Exception):
"""
The response had any HTTP status code other than 206 (Partial Content).
May be raised when calling
:meth:`~range_streams.request.RangeRequest.raise_for_non_partial_content`
"""
def __init__(self, *, request, response):
super().__init__(f"Got HTTP {response.status_code} not 206 (Partial Content)")
self.request = request
self.response = response