mirror of
https://forge.chapril.org/tykayn/book_generator
synced 2025-06-20 01:34:43 +02:00
⚡ - sauvegarde automatique de l'avancement du livre
This commit is contained in:
parent
de532abbb7
commit
375fbb3a7a
1814 changed files with 334236 additions and 0 deletions
|
@ -0,0 +1,317 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import collections.abc as cabc
|
||||
import typing as t
|
||||
|
||||
from ..http import dump_header
|
||||
from ..http import parse_dict_header
|
||||
from ..http import quote_header_value
|
||||
from .structures import CallbackDict
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
import typing_extensions as te
|
||||
|
||||
|
||||
class Authorization:
|
||||
"""Represents the parts of an ``Authorization`` request header.
|
||||
|
||||
:attr:`.Request.authorization` returns an instance if the header is set.
|
||||
|
||||
An instance can be used with the test :class:`.Client` request methods' ``auth``
|
||||
parameter to send the header in test requests.
|
||||
|
||||
Depending on the auth scheme, either :attr:`parameters` or :attr:`token` will be
|
||||
set. The ``Basic`` scheme's token is decoded into the ``username`` and ``password``
|
||||
parameters.
|
||||
|
||||
For convenience, ``auth["key"]`` and ``auth.key`` both access the key in the
|
||||
:attr:`parameters` dict, along with ``auth.get("key")`` and ``"key" in auth``.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
The ``token`` parameter and attribute was added to support auth schemes that use
|
||||
a token instead of parameters, such as ``Bearer``.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
The object is no longer a ``dict``.
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
The object is an immutable dict.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
auth_type: str,
|
||||
data: dict[str, str | None] | None = None,
|
||||
token: str | None = None,
|
||||
) -> None:
|
||||
self.type = auth_type
|
||||
"""The authorization scheme, like ``basic``, ``digest``, or ``bearer``."""
|
||||
|
||||
if data is None:
|
||||
data = {}
|
||||
|
||||
self.parameters = data
|
||||
"""A dict of parameters parsed from the header. Either this or :attr:`token`
|
||||
will have a value for a given scheme.
|
||||
"""
|
||||
|
||||
self.token = token
|
||||
"""A token parsed from the header. Either this or :attr:`parameters` will have a
|
||||
value for a given scheme.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
|
||||
def __getattr__(self, name: str) -> str | None:
|
||||
return self.parameters.get(name)
|
||||
|
||||
def __getitem__(self, name: str) -> str | None:
|
||||
return self.parameters.get(name)
|
||||
|
||||
def get(self, key: str, default: str | None = None) -> str | None:
|
||||
return self.parameters.get(key, default)
|
||||
|
||||
def __contains__(self, key: str) -> bool:
|
||||
return key in self.parameters
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, Authorization):
|
||||
return NotImplemented
|
||||
|
||||
return (
|
||||
other.type == self.type
|
||||
and other.token == self.token
|
||||
and other.parameters == self.parameters
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_header(cls, value: str | None) -> te.Self | None:
|
||||
"""Parse an ``Authorization`` header value and return an instance, or ``None``
|
||||
if the value is empty.
|
||||
|
||||
:param value: The header value to parse.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
if not value:
|
||||
return None
|
||||
|
||||
scheme, _, rest = value.partition(" ")
|
||||
scheme = scheme.lower()
|
||||
rest = rest.strip()
|
||||
|
||||
if scheme == "basic":
|
||||
try:
|
||||
username, _, password = base64.b64decode(rest).decode().partition(":")
|
||||
except (binascii.Error, UnicodeError):
|
||||
return None
|
||||
|
||||
return cls(scheme, {"username": username, "password": password})
|
||||
|
||||
if "=" in rest.rstrip("="):
|
||||
# = that is not trailing, this is parameters.
|
||||
return cls(scheme, parse_dict_header(rest), None)
|
||||
|
||||
# No = or only trailing =, this is a token.
|
||||
return cls(scheme, None, rest)
|
||||
|
||||
def to_header(self) -> str:
|
||||
"""Produce an ``Authorization`` header value representing this data.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
if self.type == "basic":
|
||||
value = base64.b64encode(
|
||||
f"{self.username}:{self.password}".encode()
|
||||
).decode("ascii")
|
||||
return f"Basic {value}"
|
||||
|
||||
if self.token is not None:
|
||||
return f"{self.type.title()} {self.token}"
|
||||
|
||||
return f"{self.type.title()} {dump_header(self.parameters)}"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.to_header()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{type(self).__name__} {self.to_header()}>"
|
||||
|
||||
|
||||
class WWWAuthenticate:
|
||||
"""Represents the parts of a ``WWW-Authenticate`` response header.
|
||||
|
||||
Set :attr:`.Response.www_authenticate` to an instance of list of instances to set
|
||||
values for this header in the response. Modifying this instance will modify the
|
||||
header value.
|
||||
|
||||
Depending on the auth scheme, either :attr:`parameters` or :attr:`token` should be
|
||||
set. The ``Basic`` scheme will encode ``username`` and ``password`` parameters to a
|
||||
token.
|
||||
|
||||
For convenience, ``auth["key"]`` and ``auth.key`` both act on the :attr:`parameters`
|
||||
dict, and can be used to get, set, or delete parameters. ``auth.get("key")`` and
|
||||
``"key" in auth`` are also provided.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
The ``token`` parameter and attribute was added to support auth schemes that use
|
||||
a token instead of parameters, such as ``Bearer``.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
The object is no longer a ``dict``.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
The ``on_update`` parameter was removed.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
auth_type: str,
|
||||
values: dict[str, str | None] | None = None,
|
||||
token: str | None = None,
|
||||
):
|
||||
self._type = auth_type.lower()
|
||||
self._parameters: dict[str, str | None] = CallbackDict(
|
||||
values, lambda _: self._trigger_on_update()
|
||||
)
|
||||
self._token = token
|
||||
self._on_update: cabc.Callable[[WWWAuthenticate], None] | None = None
|
||||
|
||||
def _trigger_on_update(self) -> None:
|
||||
if self._on_update is not None:
|
||||
self._on_update(self)
|
||||
|
||||
@property
|
||||
def type(self) -> str:
|
||||
"""The authorization scheme, like ``basic``, ``digest``, or ``bearer``."""
|
||||
return self._type
|
||||
|
||||
@type.setter
|
||||
def type(self, value: str) -> None:
|
||||
self._type = value
|
||||
self._trigger_on_update()
|
||||
|
||||
@property
|
||||
def parameters(self) -> dict[str, str | None]:
|
||||
"""A dict of parameters for the header. Only one of this or :attr:`token` should
|
||||
have a value for a given scheme.
|
||||
"""
|
||||
return self._parameters
|
||||
|
||||
@parameters.setter
|
||||
def parameters(self, value: dict[str, str]) -> None:
|
||||
self._parameters = CallbackDict(value, lambda _: self._trigger_on_update())
|
||||
self._trigger_on_update()
|
||||
|
||||
@property
|
||||
def token(self) -> str | None:
|
||||
"""A dict of parameters for the header. Only one of this or :attr:`token` should
|
||||
have a value for a given scheme.
|
||||
"""
|
||||
return self._token
|
||||
|
||||
@token.setter
|
||||
def token(self, value: str | None) -> None:
|
||||
"""A token for the header. Only one of this or :attr:`parameters` should have a
|
||||
value for a given scheme.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
self._token = value
|
||||
self._trigger_on_update()
|
||||
|
||||
def __getitem__(self, key: str) -> str | None:
|
||||
return self.parameters.get(key)
|
||||
|
||||
def __setitem__(self, key: str, value: str | None) -> None:
|
||||
if value is None:
|
||||
if key in self.parameters:
|
||||
del self.parameters[key]
|
||||
else:
|
||||
self.parameters[key] = value
|
||||
|
||||
self._trigger_on_update()
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
if key in self.parameters:
|
||||
del self.parameters[key]
|
||||
self._trigger_on_update()
|
||||
|
||||
def __getattr__(self, name: str) -> str | None:
|
||||
return self[name]
|
||||
|
||||
def __setattr__(self, name: str, value: str | None) -> None:
|
||||
if name in {"_type", "_parameters", "_token", "_on_update"}:
|
||||
super().__setattr__(name, value)
|
||||
else:
|
||||
self[name] = value
|
||||
|
||||
def __delattr__(self, name: str) -> None:
|
||||
del self[name]
|
||||
|
||||
def __contains__(self, key: str) -> bool:
|
||||
return key in self.parameters
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, WWWAuthenticate):
|
||||
return NotImplemented
|
||||
|
||||
return (
|
||||
other.type == self.type
|
||||
and other.token == self.token
|
||||
and other.parameters == self.parameters
|
||||
)
|
||||
|
||||
def get(self, key: str, default: str | None = None) -> str | None:
|
||||
return self.parameters.get(key, default)
|
||||
|
||||
@classmethod
|
||||
def from_header(cls, value: str | None) -> te.Self | None:
|
||||
"""Parse a ``WWW-Authenticate`` header value and return an instance, or ``None``
|
||||
if the value is empty.
|
||||
|
||||
:param value: The header value to parse.
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
if not value:
|
||||
return None
|
||||
|
||||
scheme, _, rest = value.partition(" ")
|
||||
scheme = scheme.lower()
|
||||
rest = rest.strip()
|
||||
|
||||
if "=" in rest.rstrip("="):
|
||||
# = that is not trailing, this is parameters.
|
||||
return cls(scheme, parse_dict_header(rest), None)
|
||||
|
||||
# No = or only trailing =, this is a token.
|
||||
return cls(scheme, None, rest)
|
||||
|
||||
def to_header(self) -> str:
|
||||
"""Produce a ``WWW-Authenticate`` header value representing this data."""
|
||||
if self.token is not None:
|
||||
return f"{self.type.title()} {self.token}"
|
||||
|
||||
if self.type == "digest":
|
||||
items = []
|
||||
|
||||
for key, value in self.parameters.items():
|
||||
if key in {"realm", "domain", "nonce", "opaque", "qop"}:
|
||||
value = quote_header_value(value, allow_token=False)
|
||||
else:
|
||||
value = quote_header_value(value)
|
||||
|
||||
items.append(f"{key}={value}")
|
||||
|
||||
return f"Digest {', '.join(items)}"
|
||||
|
||||
return f"{self.type.title()} {dump_header(self.parameters)}"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.to_header()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{type(self).__name__} {self.to_header()}>"
|
Loading…
Add table
Add a link
Reference in a new issue