On a path of improvement for my Python dev work. I have interest in testing interfaces defined with Protocol at CI/deb building time, so that if a interface isn't actually implemented by a class we will know immediately after the unit tests run.
My approach was typing with Protocol and using implements runtime_checkable to build unit test. That works, but the team got into a little debate about how to indicate a concretion was implementing a Protocol without busting runtime_checkable. In C++/Java you need inheritance to indicate implementations of interfaces, but with Python you don't necessarily need inheritance. The conversation centered on whether we should be inheriting from a Protocol interface class.
Consider this code example at the end which provides most of the gist of the question. We were thinking about Shape and indicating how to hint to a future developer that Shape is providing IShape, but doing so with inheritance makes the runtime_checkable version of isinstance unusable for its purpose in unit-testing.
There is a couple of paths to improvement here:
We could find a better way to hint that Shape implements IShape which doesn't involve direct inheritance. We could find a better way to check if an interface is implemented at test deb package build time. Maybe runtime_checkable is the wrong idea.
Anyone got guidance on how to use Python better? Thanks!
from typing import (
Protocol,
runtime_checkable
)
import dataclasses
@runtime_checkable
class IShape(Protocol):
x: float
@dataclasses.dataclass
class Shape(IShape):
foo:float = 0.
s = Shape()
# evaluates as True but doesnt provide the interface. Undermines the point of the run-time checkable in unit testing
assert isinstance(s, IShape)
print(s.x) # Error. Interface wasnt implemented
#
# Contrast with this assert
#
@dataclasses.dataclass
class Goo():
x:float = 1
@dataclasses.dataclass
class Hoo():
foo: float = 1
g = Goo()
h = Hoo()
assert isinstance(g, IShape) # asserts as true
# but because it has the interface and not because we inherited.
print(g.x)
assert isinstance(h, IShape) # asserts as False which is what we want