How to test that a python class does not subclass a protocol

Question:

I need to make sure that developers on my team are not directly subclassing a typing.Protocol instance. That is, although their classes should implement the interface of the Protocol, we don’t want:

class MyClass(MyProtocol):
    # inherits MyProtocol

which would coerce the class to be a subclass of the Protocol and, therefore, necessarily pass isinstance(MyClass(), MyProtocol) even if it does not implement any logic in the attributes and methods of the class.

Example of the problem:

class MyProtocol(Protocol):
    def foo(self):
        raise NotImplementedError("Don't subclass MyProtocol!")

class MyClass(MyProtocol):
    pass

# MyClass now has foo since it inherits the Protocol...
assert hasattr(MyClass(), 'foo')

In other words, I would like a test of the form:

def does_not_subclass_protocol(obj, protocol):
    # returns True if and only if obj does not have
    # MyClass(MyProtocol)...
    # syntax in class definition.
Asked By: philosofool

||

Answers:

You can prohibit subclassing in the Protocol and use typing.runtime_checkable:

from typing import Protocol, runtime_checkable


@runtime_checkable
class MyProtocol(Protocol):
    def foo(self): ...
    
    def __init_subclass__(cls, **kwargs):
        raise TypeError

I don’t know whether the type checker will ignore the magic method __init_subclass__ because I can only test on my mobile phone now.

Direct implementation Protocol:

>>> class MyClass:
...     def foo(self):
...         print('hello')
...
>>> isinstance(MyClass(), MyProtocol)
True

Inheritance agreement:

>>> class MyClass(MyProtocol): pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/data/user/0/ru.iiec.pydroid3/files/aarch64-linux-android/lib/python3.9/abc.py", line 106, in __new__
    cls = super().__new__(mcls, name, bases, namespace, **kwargs)
  File "<stdin>", line 6, in __init_subclass__
TypeError
Answered By: Mechanic Pig
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.