Source code for baguette.headers
import itertools
import typing
from collections.abc import Mapping, Sequence
from .types import HeadersType
from .utils import to_str
[docs]class Headers:
"""Headers implementation for handling :class:`str` or :class:`bytes` names
and values.
.. tip::
Use :func:`make_headers` to easily make a header from a :class:`list` or
a :class:`dict`.
"""
def __init__(self, *args, **kwargs):
self._headers: typing.Dict[str, str] = {}
for name, value in itertools.chain(args, kwargs.items()):
self[name] = value
[docs] def get(self, name, default=None):
"""Gets a header from its name. If not found, returns ``default``.
Arguments
---------
name: :class:`str` or :class:`bytes`
Name of the header to get.
default: Optional anything
Value to return if header not found.
Default: ``None``
Returns
-------
:class:`str`
Header value if found.
Anything
``default``'s value.
"""
name = to_str(name, encoding="ascii")
return self._headers.get(name.lower().strip(), default)
[docs] def keys(self):
"""Returns an iterator over the headers names.
Returns
-------
Iterator of :class:`str`
Iterator over the headers names.
"""
return self._headers.keys()
[docs] def items(self):
"""Returns an iterator over the headers names and values.
Returns
-------
Iterator of ``(name, value)`` :class:`tuple`
Iterator over the headers names and values.
"""
return self._headers.items()
[docs] def raw(self):
"""Returns the raw headers, a :class:`list` of ``[name, value]`` where
``name`` and ``value`` are :class:`bytes`.
Returns
-------
:class:`list` of ``[name, value]`` where ``name`` and ``value`` are\
:class:`bytes`
Raw headers for the ASGI response.
"""
return [
[name.encode("ascii"), value.encode("ascii")]
for name, value in self
]
def __str__(self) -> str:
return "\n".join(name + ": " + value for name, value in self)
def __iter__(self):
return iter(self.items())
def __len__(self):
return len(self._headers)
def __getitem__(self, name):
name = to_str(name, encoding="ascii")
return self._headers[name.lower().strip()]
def __setitem__(self, name, value):
name = to_str(name, encoding="ascii")
value = to_str(value, encoding="ascii")
self._headers[name.lower().strip()] = value.strip()
def __delitem__(self, name):
name = to_str(name, encoding="ascii")
del self._headers[name.lower().strip()]
def __contains__(self, name):
name = to_str(name, encoding="ascii")
return name.lower().strip() in self._headers
def __add__(self, other: HeadersType):
new = Headers(**self)
new += other
return new
def __iadd__(self, other: HeadersType):
other = make_headers(other)
for name, value in other:
self._headers[name] = value
return self
def __eq__(self, other: HeadersType) -> bool:
other = make_headers(other)
if len(self) != len(other):
return False
for name, value in self:
if other[name] != value:
return False
return True
[docs]def make_headers(headers: HeadersType = None) -> Headers:
"""Makes a :class:`Headers` object from a :class:`list` of ``(str, str)``
tuples, a :class:`dict`, or a :class:`Headers` instance.
Arguments
---------
headers: :class:`list` of ``(str, str)`` tuples, \
:class:`dict` or :class:`Headers`
The raw headers to convert.
Raises
------
:exc:`TypeError`
``headers`` isn't of type :class:`str`, :class:`list`,
:class:`dict`, :class:`Headers` or :obj:`None`
Returns
-------
:class:`Headers`
The converted headers.
"""
if headers is None:
headers = Headers()
elif isinstance(headers, (str, bytes)):
headers = to_str(headers, encoding="ascii")
headers = Headers(
*[header.split(":") for header in headers.splitlines()]
)
elif isinstance(headers, Sequence):
headers = Headers(*headers)
elif isinstance(headers, (Mapping, Headers)):
new_headers = Headers()
for name, value in headers.items():
new_headers[name] = value
headers = new_headers
else:
raise TypeError(
"headers must be a str, a list, a dict, a Headers instance or None"
)
return headers