Skip to content

HTTP Methods Reference — Python source

A searchable reference for every HTTP request method - GET, POST, PUT, PATCH, DELETE, and more. See at a glance which are safe, idempotent, and cacheable, then compare any two methods side by side.

This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.

"""HTTP request-method reference — pure, deterministic data + lookups.

CosmoDev polyglot showcase port of the ``http-methods`` tool, ported from
``src/lib/http-methods.ts``. Functionally equivalent to the TypeScript
original: identical inputs yield identical outputs (case-insensitive lookup,
flag + free-text filtering, and a human-readable semantic comparison report).

Property flags (safe / idempotent / cacheable / hasBody) follow RFC 9110
and the MDN reference table.

Display source — part of CosmoDev's polyglot tool pages.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional


@dataclass(frozen=True)
class MethodEntry:
    """One HTTP request method and its semantic properties.

    Frozen so records are hashable and immutable — the canonical table is a
    single source of truth that the helpers hand out references into.
    """

    method: str
    # Read-only semantics — no server state change.
    safe: bool
    # Repeating the call has the same effect as a single call.
    idempotent: bool
    # Responses may be stored by a cache (RFC 9110 / MDN).
    cacheable: bool
    # The method conventionally carries a request body.
    has_body: bool
    description: str
    typical_use: str


# The nine HTTP request methods (RFC 9110 / 9111), in canonical order.
# Stored as a tuple to make the intent of "immutable sequence" explicit.
METHODS: tuple[MethodEntry, ...] = (
    MethodEntry(
        method="GET",
        safe=True,
        idempotent=True,
        cacheable=True,
        has_body=False,
        description="Retrieves a representation of the target resource; a read-only request.",
        typical_use="Fetching a web page, reading an API resource, loading an image.",
    ),
    MethodEntry(
        method="POST",
        safe=False,
        idempotent=False,
        cacheable=True,
        has_body=True,
        description="Submits data to be processed, typically creating a new resource or triggering an action.",
        typical_use="Submitting a form, creating a record, publishing a message.",
    ),
    MethodEntry(
        method="PUT",
        safe=False,
        idempotent=True,
        cacheable=False,
        has_body=True,
        description="Replaces the target resource entirely with the request body.",
        typical_use="Updating a full record at a known URL, uploading a file by its path.",
    ),
    MethodEntry(
        method="PATCH",
        safe=False,
        idempotent=False,
        cacheable=False,
        has_body=True,
        description="Applies a partial modification to the target resource.",
        typical_use="Updating one field of a record, toggling a flag.",
    ),
    MethodEntry(
        method="DELETE",
        safe=False,
        idempotent=True,
        cacheable=False,
        has_body=False,
        description="Removes the target resource.",
        typical_use="Deleting a record or file by its URL.",
    ),
    MethodEntry(
        method="HEAD",
        safe=True,
        idempotent=True,
        cacheable=True,
        has_body=False,
        description="Identical to GET but returns only the response headers, no body.",
        typical_use="Checking existence, size, or freshness before downloading.",
    ),
    MethodEntry(
        method="OPTIONS",
        safe=True,
        idempotent=True,
        cacheable=False,
        has_body=False,
        description="Describes the communication options for the target resource.",
        typical_use="CORS preflight requests, discovering allowed methods.",
    ),
    MethodEntry(
        method="CONNECT",
        safe=False,
        idempotent=False,
        cacheable=False,
        has_body=False,
        description="Establishes a tunnel to the server (used with TLS/HTTPS proxies).",
        typical_use="Proxying encrypted connections through an intermediary.",
    ),
    MethodEntry(
        method="TRACE",
        safe=True,
        idempotent=True,
        cacheable=False,
        has_body=False,
        description="Performs a message loop-back test along the path to the target (debugging only).",
        typical_use="Diagnosing request transformations by intermediaries.",
    ),
)


@dataclass
class MethodFilter:
    """Narrowing options for :func:`filter_methods`.

    Each boolean is ``Optional[bool]`` so callers can distinguish "constrain
    to ``False``" from "no constraint" — a tri-state a bare ``bool`` cannot
    express. ``None`` (the default) means *no constraint*.
    """

    safe: Optional[bool] = None
    idempotent: Optional[bool] = None
    cacheable: Optional[bool] = None
    # Free text matched case-insensitively against method, description, typical_use.
    query: Optional[str] = None


@dataclass
class MethodComparison:
    """Semantic diff between two methods."""

    # Both methods share the `safe` flag.
    same_safety: bool
    # Both methods share the `idempotent` flag.
    same_idempotence: bool
    # One sentence per mismatched property (safe, idempotent, cacheable, has_body).
    differences: list[str] = field(default_factory=list)


def get_method(name: str) -> Optional[MethodEntry]:
    """Case-insensitive single-method lookup.

    Returns the entry, or ``None`` when unknown — the Python analogue of
    TS's ``MethodEntry | null``.
    """
    n = name.strip().upper()
    return next((m for m in METHODS if m.method == n), None)


def filter_methods(opts: Optional[MethodFilter] = None) -> list[MethodEntry]:
    """Filter the method set by boolean flags and an optional text query.

    A method must satisfy *every* present flag AND, when a query is given,
    match it in at least one of {method, description, typical_use}.
    """
    opts = opts if opts is not None else MethodFilter()
    q = (opts.query or "").strip().lower()

    def matches(m: MethodEntry) -> bool:
        # Each present flag is an AND constraint; None means skip.
        if opts.safe is not None and m.safe != opts.safe:
            return False
        if opts.idempotent is not None and m.idempotent != opts.idempotent:
            return False
        if opts.cacheable is not None and m.cacheable != opts.cacheable:
            return False
        if q:
            return (
                q in m.method.lower()
                or q in m.description.lower()
                or q in m.typical_use.lower()
            )
        return True

    return [m for m in METHODS if matches(m)]


def compare_methods(a: MethodEntry, b: MethodEntry) -> MethodComparison:
    """Compare two methods, surfacing where their semantics agree and differ.

    ``differences`` lists every mismatched property (safe, idempotent,
    cacheable, has_body) as a human-readable sentence, reproducing the
    canonical phrasing verbatim so output stays identical across ports.
    """
    differences: list[str] = []

    if a.safe != b.safe:
        differences.append(
            f"{a.method} is {_safe_word(a.safe)}, {b.method} is {_safe_word(b.safe)}."
        )
    if a.idempotent != b.idempotent:
        differences.append(
            f"{a.method} is {_idempotent_word(a.idempotent)}, "
            f"{b.method} is {_idempotent_word(b.idempotent)}."
        )
    if a.cacheable != b.cacheable:
        differences.append(
            f"{a.method} is {_cacheable_word(a.cacheable)}, "
            f"{b.method} is {_cacheable_word(b.cacheable)}."
        )
    if a.has_body != b.has_body:
        differences.append(
            f"{a.method} {_body_word(a.has_body)}, {b.method} {_body_word(b.has_body)}."
        )

    return MethodComparison(
        same_safety=a.safe == b.safe,
        same_idempotence=a.idempotent == b.idempotent,
        differences=differences,
    )


# Phrasing helpers keep the canonical difference-sentence wording in one
# place, so the output stays in lock-step across every polyglot port.
def _safe_word(value: bool) -> str:
    return "safe" if value else "not safe"


def _idempotent_word(value: bool) -> str:
    return "idempotent" if value else "not idempotent"


def _cacheable_word(value: bool) -> str:
    return "cacheable" if value else "not cacheable"


def _body_word(value: bool) -> str:
    return "takes a body" if value else "does not take a body"

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →