Source code for pcapkit.const.http.method

# -*- coding: utf-8 -*-
# pylint: disable=line-too-long
"""HTTP Method
=================

.. module:: pcapkit.const.http.method

This module contains the constant enumeration for **HTTP Method**,
which is automatically generated from :class:`pcapkit.vendor.http.method.Method`.

"""

from typing import TYPE_CHECKING

from aenum import StrEnum

from pcapkit.corekit.enum import EnumRegistry
from pcapkit.utilities.exceptions import EnumValueError

if TYPE_CHECKING:
    from typing import Any, Optional, Type

__all__ = ['Method']

[docs] class Method(EnumRegistry, StrEnum): """[Method] HTTP Method""" if TYPE_CHECKING: #: Safe method. safe: 'bool' #: Idempotent method. idempotent: 'bool' def __new__(cls, value: 'str', safe: 'bool' = False, idempotent: 'bool' = False) -> 'Type[Method]': """Create a member from a registry row. Args: value: Method token, which is also the member's value. safe: Whether the method is safe. idempotent: Whether the method is idempotent. """ obj = str.__new__(cls, value) obj._value_ = value obj.safe = safe obj.idempotent = idempotent return obj def __repr__(self) -> 'str': return f'<{self.__class__.__name__}.{self._value_}>' #: ACL [:rfc:`3744#section-8.1`] ACL = 'ACL', False, True #: BASELINE-CONTROL [:rfc:`3253#section-12.6`] BASELINE_CONTROL = 'BASELINE-CONTROL', False, True #: BIND [:rfc:`5842#section-4`] BIND = 'BIND', False, True #: CHECKIN [:rfc:`3253#section-9.4`] CHECKIN = 'CHECKIN', False, True #: CHECKOUT [:rfc:`3253#section-8.8`] CHECKOUT = 'CHECKOUT', False, True #: CONNECT [:rfc:`9110#section-9.3.6`] CONNECT = 'CONNECT', False, False #: COPY [:rfc:`4918#section-9.8`] COPY = 'COPY', False, True #: DELETE [:rfc:`9110#section-9.3.5`] DELETE = 'DELETE', False, True #: GET [:rfc:`9110#section-9.3.1`] GET = 'GET', True, True #: HEAD [:rfc:`9110#section-9.3.2`] HEAD = 'HEAD', True, True #: LABEL [:rfc:`3253#section-8.2`] LABEL = 'LABEL', False, True #: LINK [:rfc:`2068#section-19.6.1.2`] LINK = 'LINK', False, True #: LOCK [:rfc:`4918#section-9.10`] LOCK = 'LOCK', False, False #: MERGE [:rfc:`3253#section-11.2`] MERGE = 'MERGE', False, True #: MKACTIVITY [:rfc:`3253#section-13.5`] MKACTIVITY = 'MKACTIVITY', False, True #: MKCALENDAR [:rfc:`4791#section-5.3.1`][:rfc:`8144#section-2.3`] MKCALENDAR = 'MKCALENDAR', False, True #: MKCOL #: [:rfc:`4918#section-9.3`][:rfc:`5689#section-3`][:rfc:`8144#section-2.3`] MKCOL = 'MKCOL', False, True #: MKREDIRECTREF [:rfc:`4437#section-6`] MKREDIRECTREF = 'MKREDIRECTREF', False, True #: MKWORKSPACE [:rfc:`3253#section-6.3`] MKWORKSPACE = 'MKWORKSPACE', False, True #: MOVE [:rfc:`4918#section-9.9`] MOVE = 'MOVE', False, True #: OPTIONS [:rfc:`9110#section-9.3.7`] OPTIONS = 'OPTIONS', True, True #: ORDERPATCH [:rfc:`3648#section-7`] ORDERPATCH = 'ORDERPATCH', False, True #: PATCH [:rfc:`5789#section-2`] PATCH = 'PATCH', False, False #: POST [:rfc:`9110#section-9.3.3`] POST = 'POST', False, False #: PRI [:rfc:`9113#section-3.4`] PRI = 'PRI', True, True #: PROPFIND [:rfc:`4918#section-9.1`][:rfc:`8144#section-2.1`] PROPFIND = 'PROPFIND', True, True #: PROPPATCH [:rfc:`4918#section-9.2`][:rfc:`8144#section-2.2`] PROPPATCH = 'PROPPATCH', False, True #: PUT [:rfc:`9110#section-9.3.4`] PUT = 'PUT', False, True #: QUERY [:rfc:`10008#section-2`] QUERY = 'QUERY', True, True #: REBIND [:rfc:`5842#section-6`] REBIND = 'REBIND', False, True #: REPORT [:rfc:`3253#section-3.6`][:rfc:`8144#section-2.1`] REPORT = 'REPORT', True, True #: SEARCH [:rfc:`5323#section-2`] SEARCH = 'SEARCH', True, True #: TRACE [:rfc:`9110#section-9.3.8`] TRACE = 'TRACE', True, True #: UNBIND [:rfc:`5842#section-5`] UNBIND = 'UNBIND', False, True #: UNCHECKOUT [:rfc:`3253#section-4.5`] UNCHECKOUT = 'UNCHECKOUT', False, True #: UNLINK [:rfc:`2068#section-19.6.1.3`] UNLINK = 'UNLINK', False, True #: UNLOCK [:rfc:`4918#section-9.11`] UNLOCK = 'UNLOCK', False, True #: UPDATE [:rfc:`3253#section-7.1`] UPDATE = 'UPDATE', False, True #: UPDATEREDIRECTREF [:rfc:`4437#section-7`] UPDATEREDIRECTREF = 'UPDATEREDIRECTREF', False, True #: VERSION-CONTROL [:rfc:`3253#section-3.5`] VERSION_CONTROL = 'VERSION-CONTROL', False, True
[docs] @classmethod def _unregistered_member(cls, value: 'str', name: 'str') -> 'Method': """Build a member absent from this registry's own lookup tables. Leaves :attr:`safe` and :attr:`idempotent` at the same defaults :meth:`__new__` itself would, rather than missing entirely -- :meth:`~pcapkit.corekit.enum.EnumRegistry._unregistered_member` bypasses :meth:`__new__` (it calls :class:`str`'s directly), so those attributes would otherwise be absent. There is no more specific value to reconstruct them from -- a bare wire method verb carries neither, which is exactly the owner's reasoning for why ``get()``/``_missing_`` must not mint one: :meth:`register` is the path that can actually supply them. Args: value: Value to get enum item -- the convention here, shared with :class:`~pcapkit.const.ftp.command.FEATCode` and :class:`~pcapkit.const.ftp.command.Command`, is the caller's own casing, unchanged: an unregistered member's *value* is exactly what was observed on the wire, matching how a *registered* member's own value is exactly what was declared, never reformatted. Only :attr:`name` -- the identifier, not the value -- is canonicalised. A *registered* member carries its value the same way: :meth:`__new__` passes ``value`` to ``str.__new__``, as :class:`~pcapkit.const.ftp.command.Command`'s own ``__new__`` does (:issue:`870`), so both kinds of member carry their own value as real :class:`str` content (``str(Method.GET) == 'GET'`` and ``str(Method('frob')) == 'frob'``). name: Bare label for the unregistered member -- here, the canonical upper-case form of ``value``, matching the name every *registered* member of this class is looked up by, since :issue:`860`'s open-vocabulary registries have no manufactured placeholder label to fall back to. """ obj = super()._unregistered_member(value, name) obj.safe = False obj.idempotent = False return obj
[docs] @classmethod def get(cls, key: 'str', default: 'Optional[str]' = None) -> 'Method': """Resolve ``key`` to a member, matching the method token case-sensitively. Delegates to :meth:`~pcapkit.corekit.enum.EnumLookup.get` for the lookup itself, so ``key`` is checked against member values as well as names (GitHub issue :issue:`908`). That is what resolves the two IANA methods whose member *name* differs from their *value* -- ``BASELINE_CONTROL`` / ``'BASELINE-CONTROL'`` and ``VERSION_CONTROL`` / ``'VERSION-CONTROL'``, a hyphen being unusable in a Python identifier -- which the constructor also finds by value, before it falls back to :meth:`_missing_`. It is a :class:`classmethod`, as the base is, because a zero-argument ``super()`` needs a first argument to bind (:issue:`908`). ``Method.get('X')`` binds the same as it would on a :class:`staticmethod`. ``default`` does not follow the base's ``NO_DEFAULT`` sentinel and value-only fallback: the base's ``default`` must name a *registered* value (resolved through ``_value2member_map_``), while this override's ``default`` supplies the *value* of the unregistered member it builds on a miss. Args: key: Key to get enum item. Looked up case-**sensitively**, per :rfc:`9110#section-9.1` -- the method token is case-sensitive, unlike :meth:`~pcapkit.const.ftp.command.Command.get`'s equivalent override, which stays case-insensitive because :rfc:`959#section-5` says FTP command codes are not. Checked against both member names and values, via the base's own precedence -- name before value -- so a value-only match such as ``'BASELINE-CONTROL'`` resolves too (:issue:`908`). default: Value for the unregistered member built when ``key`` matches neither a name nor a value. ``None``, the default, uses ``key`` itself. Returns: The member whose name or value is ``key``. On a miss, an unregistered member named ``key`` upper-cased, with the value ``default`` (or ``key``), which is not added to the lookup tables, so ``get`` never registers a method (:issue:`860`). Raises: ValueError: If ``key`` is not a :class:`str`. This reaches the caller from the base's non-``str`` branch, where :meth:`_validate_value` refuses it before ``cls(key)`` could reach ``_missing_``, and is **not** caught by the miss fallback described under *Returns* -- only a failed lookup is. Raising is the intended behaviour for ``get(b'GET')`` as well, since no member of this registry has a :class:`bytes` name or value. """ try: return super().get(key) except KeyError: # NOTE: the value is ``default`` if the caller supplied one, or # else ``key`` exactly as given -- never its upper-cased form -- # so an unregistered member's value is the caller's own casing, # the same convention :meth:`_unregistered_member` documents and # :class:`~pcapkit.const.ftp.command.FEATCode` follows. # The name is always the canonical upper-case form, matching # :meth:`_missing_` and every registered member's own name. Two # calls naming the same method in different case, e.g. # ``get('frob')`` and ``get('FROB')``, therefore build results # that are *not* equal -- each is exactly what its own caller # passed, since ``get`` does not mint (GitHub issue #860). return cls._unregistered_member(default if default is not None else key, key.upper())
[docs] @classmethod def _validate_value(cls, value: 'Any') -> 'None': """Reject ``value`` unless it is a :class:`str`, as :meth:`_missing_` does. Overrides :meth:`~pcapkit.corekit.enum.EnumLookup._validate_value` with the check :meth:`_missing_` makes, and the message it raises, so :meth:`~pcapkit.corekit.enum.EnumRegistry.register` refuses exactly the values :meth:`get` refuses, in the same words (:issue:`1816`). Any :class:`str` passes, since :meth:`get` and :meth:`_missing_` resolve every one. Args: value: Value about to be looked up or minted. Raises: EnumValueError: If ``value`` is not a :class:`str`. A :exc:`ValueError`, so an ``except ValueError`` caller is unaffected. """ if not isinstance(value, str): raise EnumValueError(f'{value!r} is not a valid {cls.__name__}')
[docs] @classmethod def _missing_(cls, value: 'str') -> 'Method': """Lookup function used when value is not found. Args: value: Value to get enum item. Matched case-insensitively against the canonical upper-case member names, then against member values, so ``'baseline-control'`` resolves to ``BASELINE_CONTROL`` (:issue:`2284`) -- deliberately unlike :meth:`get`, which is case-**sensitive** per :rfc:`9110#section-9.1`. The split was ruled deliberate on GitHub issue :issue:`896`; GitHub issue :issue:`908` is the pointer between the two, so the two rationales are not left contradicting each other with nothing tying them together. """ if not isinstance(value, str): raise ValueError(f'{value!r} is not a valid {cls.__name__}') name = value.upper() if name in cls._member_map_: return cls._member_map_[name] if name in cls._value2member_map_: return cls._value2member_map_[name] return cls._unregistered_member(value, name)