Protocols Check Names Not Signatures

BadWriter.write takes no arguments besides self, which does not match the Writer protocol's write(self, data: str) -> None signature:

from typing import Protocol, runtime_checkable

@runtime_checkable
class Writer(Protocol):
    def write(self, data: str) -> None: ...

class BadWriter:
    def write(self) -> int:
        return 42

def send(w: Writer, payload: str) -> None:
    w.write(payload)

bw = BadWriter()
print(isinstance(bw, Writer))
send(bw, "hello")

Neither the w: Writer annotation nor payload: str is checked by the interpreter. What happens when this runs?

A

It prints True, then send completes silently and returns None, because Python only checks that an attribute named write exists, and never actually calls it with a mismatched argument count since the Writer annotation already vouched for compatibility.

B

It prints True, then raises TypeError: BadWriter.write() takes 1 positional argument but 2 were given on the call inside send. @runtime_checkable makes isinstance() check only that a write attribute exists on the object, ignoring its parameter list and return type, so bw passes despite the mismatched signature. The real failure shows up later, as an ordinary argument-count error at the point w.write(payload) actually executes, since nothing here runs a type checker.

C

It prints False, then raises TypeError: object does not implement Writer protocol on the call inside send, because @runtime_checkable compares full method signatures, not just method names.

D

It raises TypeError: Protocol classes can only be used with isinstance() if the concrete class also declares @runtime_checkable on the isinstance(bw, Writer) line, since BadWriter never subclasses Writer.