Protocol for alarm handler plugins — structural typing without ABC lock-in
If it has notify(alarm_row), it is a handler. Inheritance is optional.
If it has notify(alarm_row), it is a handler. Inheritance is optional.
§I — Frame
Pure Python depth for the Python track: typing.Protocol (and @runtime_checkable when needed) to describe alarm handler plugins. Ops produces census dicts; handlers send SNS, open tickets, or no-op in dry-run. Not TypedDict (09-07) and not deepcopy (09-04).
§II — Protocol sketch
from typing import Protocol, runtime_checkable
class AlarmRow(Protocol):
name: str
state: str
actions: int
@runtime_checkable
class AlarmHandler(Protocol):
def notify(self, row: AlarmRow) -> None: ...
A concrete class needs no explicit base if methods match. Mypy/pyright check structurally.
§III — Why not only ABC
ABCs force import-time coupling to a shared base package. Protocols let a thin ops script accept third-party handlers that never imported your base. Use ABC when you also need registration helpers or isinstance without @runtime_checkable.
§IV — Worked dispatch
def dispatch(rows: list[AlarmRow], handlers: list[AlarmHandler]) -> None:
for row in rows:
if row.actions == 0 and row.state == "ALARM":
for h in handlers:
h.notify(row)
Handlers stay side-effectful; the Protocol stays tiny.
§V — Pitfalls
- Protocols with attributes vs
@propertymismatch under some checkers. @runtime_checkableonly checks methods present, not attribute types.- Do not confuse Protocol with TypedDict (shapes of dicts vs objects with methods).
§VI — Closing
Structural typing keeps the census script open for new sinks without rewriting the core loop.
Related
- Ops
- Cert
- Go