Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
auth.py321 linesDownload Raw Back to datastructures
1from __future__ import annotations2 3import base644import binascii5import collections.abc as cabc6import typing as t7 8from ..http import dump_header9from ..http import parse_dict_header10from ..http import quote_header_value11from .structures import CallbackDict12 13if t.TYPE_CHECKING:14    import typing_extensions as te15 16 17class Authorization:18    """Represents the parts of an ``Authorization`` request header.19 20    :attr:`.Request.authorization` returns an instance if the header is set.21 22    An instance can be used with the test :class:`.Client` request methods' ``auth``23    parameter to send the header in test requests.24 25    Depending on the auth scheme, either :attr:`parameters` or :attr:`token` will be26    set. The ``Basic`` scheme's token is decoded into the ``username`` and ``password``27    parameters.28 29    For convenience, ``auth["key"]`` and ``auth.key`` both access the key in the30    :attr:`parameters` dict, along with ``auth.get("key")`` and ``"key" in auth``.31 32    .. versionchanged:: 2.333        The ``token`` parameter and attribute was added to support auth schemes that use34        a token instead of parameters, such as ``Bearer``.35 36    .. versionchanged:: 2.337        The object is no longer a ``dict``.38 39    .. versionchanged:: 0.540        The object is an immutable dict.41    """42 43    def __init__(44        self,45        auth_type: str,46        data: dict[str, str | None] | None = None,47        token: str | None = None,48    ) -> None:49        self.type = auth_type50        """The authorization scheme, like ``basic``, ``digest``, or ``bearer``."""51 52        if data is None:53            data = {}54 55        self.parameters = data56        """A dict of parameters parsed from the header. Either this or :attr:`token`57        will have a value for a given scheme.58        """59 60        self.token = token61        """A token parsed from the header. Either this or :attr:`parameters` will have a62        value for a given scheme.63 64        .. versionadded:: 2.365        """66 67    def __getattr__(self, name: str) -> str | None:68        return self.parameters.get(name)69 70    def __getitem__(self, name: str) -> str | None:71        return self.parameters.get(name)72 73    def get(self, key: str, default: str | None = None) -> str | None:74        return self.parameters.get(key, default)75 76    def __contains__(self, key: str) -> bool:77        return key in self.parameters78 79    def __eq__(self, other: object) -> bool:80        if not isinstance(other, Authorization):81            return NotImplemented82 83        return (84            other.type == self.type85            and other.token == self.token86            and other.parameters == self.parameters87        )88 89    @classmethod90    def from_header(cls, value: str | None) -> te.Self | None:91        """Parse an ``Authorization`` header value and return an instance, or ``None``92        if the value is empty.93 94        :param value: The header value to parse.95 96        .. versionadded:: 2.397        """98        if not value:99            return None100 101        scheme, _, rest = value.partition(" ")102        scheme = scheme.lower()103        rest = rest.strip()104 105        if scheme == "basic":106            try:107                username, _, password = base64.b64decode(rest).decode().partition(":")108            except (binascii.Error, UnicodeError):109                return None110 111            return cls(scheme, {"username": username, "password": password})112 113        if "=" in rest.rstrip("="):114            # = that is not trailing, this is parameters.115            return cls(scheme, parse_dict_header(rest), None)116 117        # No = or only trailing =, this is a token.118        return cls(scheme, None, rest)119 120    def to_header(self) -> str:121        """Produce an ``Authorization`` header value representing this data.122 123        .. versionadded:: 2.0124        """125        if self.type == "basic":126            value = base64.b64encode(127                f"{self.username}:{self.password}".encode()128            ).decode("ascii")129            return f"Basic {value}"130 131        if self.token is not None:132            return f"{self.type.title()} {self.token}"133 134        return f"{self.type.title()} {dump_header(self.parameters)}"135 136    def __str__(self) -> str:137        return self.to_header()138 139    def __repr__(self) -> str:140        return f"<{type(self).__name__} {self.to_header()}>"141 142 143class WWWAuthenticate:144    """Represents the parts of a ``WWW-Authenticate`` response header.145 146    Set :attr:`.Response.www_authenticate` to an instance of list of instances to set147    values for this header in the response. Modifying this instance will modify the148    header value.149 150    Depending on the auth scheme, either :attr:`parameters` or :attr:`token` should be151    set. The ``Basic`` scheme will encode ``username`` and ``password`` parameters to a152    token.153 154    For convenience, ``auth["key"]`` and ``auth.key`` both act on the :attr:`parameters`155    dict, and can be used to get, set, or delete parameters. ``auth.get("key")`` and156    ``"key" in auth`` are also provided.157 158    .. versionchanged:: 2.3159        The ``token`` parameter and attribute was added to support auth schemes that use160        a token instead of parameters, such as ``Bearer``.161 162    .. versionchanged:: 2.3163        The object is no longer a ``dict``.164 165    .. versionchanged:: 2.3166        The ``on_update`` parameter was removed.167    """168 169    def __init__(170        self,171        auth_type: str,172        values: dict[str, str | None] | None = None,173        token: str | None = None,174    ):175        self._type = auth_type.lower()176        self._parameters: dict[str, str | None] = CallbackDict(177            values, lambda _: self._trigger_on_update()178        )179        self._token = token180        self._on_update: cabc.Callable[[WWWAuthenticate], None] | None = None181 182    def _trigger_on_update(self) -> None:183        if self._on_update is not None:184            self._on_update(self)185 186    @property187    def type(self) -> str:188        """The authorization scheme, like ``basic``, ``digest``, or ``bearer``."""189        return self._type190 191    @type.setter192    def type(self, value: str) -> None:193        self._type = value194        self._trigger_on_update()195 196    @property197    def parameters(self) -> dict[str, str | None]:198        """A dict of parameters for the header. Only one of this or :attr:`token` should199        have a value for a given scheme.200        """201        return self._parameters202 203    @parameters.setter204    def parameters(self, value: dict[str, str]) -> None:205        self._parameters = CallbackDict(value, lambda _: self._trigger_on_update())206        self._trigger_on_update()207 208    @property209    def token(self) -> str | None:210        """A dict of parameters for the header. Only one of this or :attr:`token` should211        have a value for a given scheme.212        """213        return self._token214 215    @token.setter216    def token(self, value: str | None) -> None:217        """A token for the header. Only one of this or :attr:`parameters` should have a218        value for a given scheme.219 220        .. versionadded:: 2.3221        """222        self._token = value223        self._trigger_on_update()224 225    def __getitem__(self, key: str) -> str | None:226        return self.parameters.get(key)227 228    def __setitem__(self, key: str, value: str | None) -> None:229        if value is None:230            if key in self.parameters:231                del self.parameters[key]232        else:233            self.parameters[key] = value234 235        self._trigger_on_update()236 237    def __delitem__(self, key: str) -> None:238        if key in self.parameters:239            del self.parameters[key]240            self._trigger_on_update()241 242    def __getattr__(self, name: str) -> str | None:243        return self[name]244 245    def __setattr__(self, name: str, value: str | None) -> None:246        if name in {"_type", "_parameters", "_token", "_on_update"}:247            super().__setattr__(name, value)248        else:249            self[name] = value250 251    def __delattr__(self, name: str) -> None:252        del self[name]253 254    def __contains__(self, key: str) -> bool:255        return key in self.parameters256 257    def __eq__(self, other: object) -> bool:258        if not isinstance(other, WWWAuthenticate):259            return NotImplemented260 261        return (262            other.type == self.type263            and other.token == self.token264            and other.parameters == self.parameters265        )266 267    def get(self, key: str, default: str | None = None) -> str | None:268        return self.parameters.get(key, default)269 270    @classmethod271    def from_header(cls, value: str | None) -> te.Self | None:272        """Parse a ``WWW-Authenticate`` header value and return an instance, or ``None``273        if the value is empty.274 275        :param value: The header value to parse.276 277        .. versionadded:: 2.3278        """279        if not value:280            return None281 282        scheme, _, rest = value.partition(" ")283        scheme = scheme.lower()284        rest = rest.strip()285 286        if "=" in rest.rstrip("="):287            # = that is not trailing, this is parameters.288            return cls(scheme, parse_dict_header(rest), None)289 290        # No = or only trailing =, this is a token.291        return cls(scheme, None, rest)292 293    def to_header(self) -> str:294        """Produce a ``WWW-Authenticate`` header value representing this data."""295        if self.token is not None:296            return f"{self.type.title()} {self.token}"297 298        if not self.parameters:299            return self.type.title()300 301        if self.type == "digest":302            items = []303 304            for key, value in self.parameters.items():305                if key in {"realm", "domain", "nonce", "opaque", "qop"}:306                    value = quote_header_value(value, allow_token=False)307                else:308                    value = quote_header_value(value)309 310                items.append(f"{key}={value}")311 312            return f"Digest {', '.join(items)}"313 314        return f"{self.type.title()} {dump_header(self.parameters)}"315 316    def __str__(self) -> str:317        return self.to_header()318 319    def __repr__(self) -> str:320        return f"<{type(self).__name__} {self.to_header()}>"321 
codekingpro/portable-devtools · Team Ai