🌐
Python
typing.python.org › en › latest › spec › protocol.html
Protocols — typing documentation
However, it should be possible for protocol types to implement custom instance and class checks when this makes sense, similar to how Iterable and other ABCs in collections.abc and typing already do it, but this is limited to non-generic and unsubscripted generic protocols (Iterable is statically equivalent to Iterable[Any]). The typing module will define a special @runtime_checkable class decorator that provides the same semantics for class and instance checks as for collections.abc classes, essentially making them “runtime protocols”:
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
1 month ago - See PEP 544 for more details. Protocol classes decorated with runtime_checkable() (described later) act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures.
Discussions

Is there a downside to `typing.runtime_checkable`?
I can’t find one, and I’m curious why the capability wasn’t just added to the Protocol type by default! from typing import Protocol, runtime_checkable @runtime_checkable class Runnable(Protocol): """This is nothing new!""" def __run__(self) -> None: """Run something.""" class RoadRunner: ... More on discuss.python.org
🌐 discuss.python.org
0
0
November 4, 2022
`isinstance` on `runtime_checkable` `Protocol` has side-effects for `@property` methods
from typing import Protocol, runtime_checkable @runtime_checkable class X(Protocol): @property def myproperty(self): ... More on github.com
🌐 github.com
29
March 5, 2023
isinstance check of runtime_checkable Protocol returns true for data-protocol if explicitly subclassed without non-method member
Asked this question on stackoverflow but I am copying here as this seems to be an untended results based on PEP 544. If a protocol is implemented with a requried attribute i.e. name @runtime_checka... More on github.com
🌐 github.com
3
January 4, 2021
Instance checking with data Protocols?
Hi! I’m having a little trouble understanding what I can do with the new runtime checkable protocols. The PEP says that data protocols (those with typed attributes) can be checked with isinstance, so I’ve tried this: from typing import Protocol, runtime_checkable @runtime_checkable class ... More on discuss.python.org
🌐 discuss.python.org
0
1
November 28, 2019
🌐
Mypy
mypy.readthedocs.io › en › stable › protocols.html
Protocols and structural subtyping - mypy 1.19.1 documentation
You can use a protocol class with isinstance() if you decorate it with the @runtime_checkable class decorator.
🌐
Python.org
discuss.python.org › python help
Is there a downside to `typing.runtime_checkable`? - Python Help - Discussions on Python.org
November 4, 2022 - Is there a downside to adding the runtime_checkable decorator to a Protocol definition? I can’t find one, and I’m curious why the capability wasn’t just added to the Protocol type by default! from typing import Protocol, runtime_checkable @runtime_checkable class Runnable(Protocol): """This is nothing new!""" def __run__(self) -> None: """Run something.""" class RoadRunner: """A concrete implementation.""" def __run__(self) -> None: """Run!""" bird = RoadRunner() assert isin...
🌐
Pybites
pybit.es › articles › typing-protocol-abc-alternative
Leveraging typing.Protocol: Faster Error Detection And Beyond Inheritance – Pybites
February 9, 2024 - This feature allows protocols to be used in runtime type checks, similar to abstract base classes. Applying @typing.runtime_checkable to a protocol makes it possible to use isinstance() and issubclass() checks, offering a layer of dynamic validation ...
🌐
Runebook.dev
runebook.dev › en › docs › python › library › typing › typing.runtime_checkable
A Friendly Guide to Python's typing.runtime_checkable()
When you decorate a Protocol with @runtime_checkable, you tell the Python interpreter to perform a best-effort check at runtime to see if an object implements all the required methods and attributes of that Protocol.
🌐
GitHub
github.com › python › cpython › issues › 102433
`isinstance` on `runtime_checkable` `Protocol` has side-effects for `@property` methods · Issue #102433 · python/cpython
March 5, 2023 - For example: from typing import Protocol, runtime_checkable @runtime_checkable class X(Protocol): @property def myproperty(self): ... class Y: @property def myproperty(self): raise RuntimeError("hallo") isinstance(Y(), X) will raise the ...
Author   chrisjsewell
🌐
Python
typing.python.org › en › latest › reference › protocols.html
Protocols and structural subtyping — typing documentation
from typing import Protocol, runtime_checkable @runtime_checkable class Portable(Protocol): handles: int class Mug: def __init__(self) -> None: self.handles = 1 def use(handles: int) -> None: ... mug = Mug() if isinstance(mug, Portable): # Works at runtime!
Find elsewhere
🌐
Real Python
realpython.com › python-protocol
Python Protocols: Leveraging Structural Subtyping – Real Python
July 25, 2024 - The @runtime_checkable decorator marks a protocol class as a runtime protocol so that you can use it with isinstance() and issubclass().
🌐
GitHub
github.com › python › typing › issues › 800
isinstance check of runtime_checkable Protocol returns true for data-protocol if explicitly subclassed without non-method member · Issue #800 · python/typing
January 4, 2021 - If a protocol is implemented with a requried attribute i.e. name · @runtime_checkable class DuckProtocol(Protocol): """Protocol for a duck""" name: str @abstractmethod def quack(self): raise NotImplementedError
Author   mwjohnson56
🌐
Python
peps.python.org › pep-0544
PEP 544 – Protocols: Structural subtyping (static duck typing) | peps.python.org
March 5, 2017 - Add a class attribute _is_protocol = True if that is the case. Verify that a protocol class only has protocol base classes in the MRO (except for object). Implement @runtime_checkable that allows __subclasshook__() performing structural instance and subclass checks as in collections.abc classes.
🌐
Python.org
discuss.python.org › python help
Instance checking with data Protocols? - Python Help - Discussions on Python.org
November 28, 2019 - Hi! I’m having a little trouble understanding what I can do with the new runtime checkable protocols. The PEP says that data protocols (those with typed attributes) can be checked with isinstance, so I’ve tried this: from typing import Protocol, runtime_checkable @runtime_checkable class Foo(Protocol): x: int class Bar(object): def __init__(self, x): self.x = x assert isinstance(Bar(10), Foo) assert not isinstance(Bar(10.), Foo) # This fails, which I didn't expect I feel li...
🌐
OneUptime
oneuptime.com › home › blog › how to use protocol classes for type safety in python
How to Use Protocol Classes for Type Safety in Python
February 2, 2026 - # runtime_protocols.py from typing import Protocol, runtime_checkable @runtime_checkable class Closeable(Protocol): """Protocol for objects that can be closed""" def close(self) -> None: ...
🌐
Python.org
discuss.python.org › ideas
Optional strict mode for @runtime_checkable - Ideas - Discussions on Python.org
April 15, 2025 - Idea Extend @typing.runtime_checkable with optional parameters: def runtime_checkable(*, values: bool = False, signatures: bool = False, return_annotations: bool | None = None, ) values – check runtime types of attribute values in isinstance(obj, Proto) for data (non-callable) members, signatures – check member function signatures (both parameters and return annotation) in isinstance(obj, Proto) and issubclass(Cls, Proto), return_annotations – adjust checking of return annotation ...
Top answer
1 of 1
1

I'll go through the problems one by one.

Given the following protocols for file-like objects:

from typing import Any, Protocol, TypeVar, runtime_checkable

T_co = TypeVar('T_co', covariant=True)


@runtime_checkable
class SupportsRead(Protocol[T_co]):
    def read(self, __length: int = ...) -> T_co: ...


@runtime_checkable
class SupportsWrite(Protocol[T_co]):
    def write(self, data: str | bytes) -> Any: ...

SerializerToFile

Simply adjusting your protocol members' parameter names to reflect those of the corresponding functions in the json module fixes the issue:

@runtime_checkable
class SerializerToFile(Protocol):
    def dump(
        self,
        obj: Any,                        # was called `value`
        fp: SupportsWrite[str | bytes],  # was called `file`
        **kwargs: Any,                   # was missing `Any` (optional)
    ) -> None: ...

    def load(
        self,
        fp: SupportsRead[str | bytes],  # was called `file`
        **kwargs: Any,                  # was missing `Any` (optional)
    ) -> Any: ...


import json

var1: SerializerToFile = json  # passes

SerializerToString

Again, the names did not match, but also your protocol methods accept arbitrary positional arguments, which neither the loads nor the dumps function does:

@runtime_checkable
class SerializerToString(Protocol):
    def dumps(
        self,
        obj: Any,       # was called `value`
        # *argv: Any,   # this needs to be removed
        **kwargs: Any,  # was missing `Any` (optional)
    ) -> str: ...

    def loads(
        self,
        s: str | bytes,  # was called `value`
        # *argv: Any,    # this needs to be removed
        **kwargs: Any,   # was missing `Any` (optional)
    ) -> Any: ...


import json

var1: SerializerToString = json  # passes

Serializer (intersection)

With the fixed protocols from above, the intersection of both of them is also a valid annotation to use for the json module:

...

@runtime_checkable
class Serializer(SerializerToString, SerializerToFile, Protocol):
    pass


import json

var1: Serializer = json  # passes

Custom serializer

Now the custom serializer needs to be adjusted accordingly, so that it matches the protocol as well:

...

class MySerializer:
    def dumps(self, obj: Any, **_kwargs: Any) -> str:
        return f"dumps {obj}"

    def loads(self, s: str | bytes, **_kwargs: Any) -> Any:
        return f"loads {s!r}"


var1: SerializerToString = MySerializer()  # passes

The first loads argument must accept bytes as well, since the protocol defines it as str | bytes. Subtypes are contravariant in their method parameters. So you could only widen the parameter types accepted by the serializer, never narrow it. Something silly like str | bytes | tuple[Any] for the first argument on your loads method would still work, but just str does not.


Attempt at explanation

As opposed to the Callable type, where you may only define the types that parameters in certain positions of the callable take, the protocol methods are more specific, since Python does allow all "normal" arguments to a function call to be passed as keyword-arguments (i.e. by name).

The exceptions are variable-number positional arguments, defined via *args and arguments specifically defined as positional-only by having their parameters precede a / in the signature.

So if you say that something is a structural subtype of e.g. SerializerToFile, if it implements a method dumps, where the first parameter is "positional-or-keyword" and named value, you could then use an object of that type somewhere and call that method as dumps(value=123). But the json.dumps method can not be called that way because its first parameter is named obj, so a call of json.dumps(obj={}) works, whereas the call json.dumps(value={}) would fail. Therefore json does not conform to the protocol.

Similarly, if your protocol allows "var. args" in a method, i.e. dumps(obj: Any, *args: Any, **kwargs: Any), that means you might want to call that method on an instance of that type as dumps({}, 1, 2, 3, 4), but that is not allowed for json.dumps because aside from the very first parameter it explicitly defines the rest as keyword-only (all following the *).

🌐
Xebia
xebia.com › home › blog › protocols in python: why you need them
Protocols In Python: Why You Need Them | Xebia
July 25, 2022 - Can Python protocols be used at runtime? By default, protocols are only checked at compile-time. However, you can use the runtime_checkable decorator to make protocols enforceable at runtime.
🌐
Stanza
stanza.dev › courses › python-architecture › protocols › python-architecture-protocols-runtime
Runtime Checkable Protocols - Python Architecture: Patterns & Type System | Stanza
Runtime Checking The @runtime_checkable Decorator python from typing import Protocol, runtime_checkable @runtime_checkable class Closable(Protocol): def cl
🌐
Turingtaco
turingtaco.com › protocols-default-methods-inheritance-and-more
Protocols: Default Methods, Inheritance, and More
December 7, 2024 - The RuntimeChecker Protocol (lines 27-30) is marked with @runtime_checkable, enabling runtime type checking with isinstance.