We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Structural Typing and the Hard Parts step 7 of 24
Callback protocols: closing the Callable[..., Any] hole
Callable[..., Any] in a plugin registry, an event bus or a DI container is
the largest single Any-hole most codebases have — and mypy --strict will
never mention it, because the annotation is technically complete. Every
call made through that registry is unchecked: wrong argument count, wrong
types, wrong keyword names, wrong return type. All silent.
What Callable can and cannot say
collections.abc.Callable[[A, B], R] expresses positional parameters
only. There is no syntax in it for:
- keyword-only parameters
- parameter names
- optional parameters with defaults
- overloads
The moment you need any of those, the usual move is to widen to
Callable[..., Any] and lose everything. The correct move is a callback
protocol — a Protocol whose single member is __call__:
class Handler(Protocol):
def __call__(self, payload: str, /, *, retries: int = 0) -> str: ...
That expresses one positional-only payload (the / marks it), one optional
keyword-only retries, and a str return. And because parameter names are
part of a callback protocol’s contract unless marked positional-only, a
handler that spells the keyword retry instead of retries is now a static
error rather than a TypeError in production.
Your task
Define the Handler callback protocol above, three handlers matching it, and
a registry typed dict[str, Handler]:
-
shout—payload.upper() + "!" * retries -
tag—f"[{retries}]{payload}" -
trim—payload.strip() * (retries + 1)
def dispatch(name: str, payload: str, retries: int) -> str
def solve(names: list[str], payloads: list[str], retries: list[int]) -> list[str]
dispatch looks the handler up and calls it as
handler(payload, retries=retries); an unknown name returns
f"unknown:{name}". solve zips the three parallel input lists (use
strict=True) and returns one result per entry.
The payoff
With the registry typed dict[str, Handler], adding a handler whose payload
parameter is not positional-only, or whose keyword is misspelled, or which
returns bytes, fails at the point of registration — the one place a human
is looking. With Callable[..., Any] all three ship.
Loading visualization…
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.