OOP Interface Support in Python

Python currently lacks a built-in mechanism to enforce method signatures in interfaces. While the abc module allows for abstract base classes (ABCs), it does not verify method signatures, leading to potential runtime errors when method signatures do not match expectations.

A new Interface class could be introduced, using the InterfaceMeta metaclass. Any class inheriting from Interface must implement all defined methods with the same signature.

The code:

from abc import ABCMeta
import inspect

class InterfaceMeta(ABCMeta):
    """Metaclass to force implementation of methods in derived classes with the same signature."""
    
    def __new__(mcs, name, bases, namespace):
        if bases:  # Ensure we are checking derived classes, not the base interface
            for base in bases:
                if isinstance(base, InterfaceMeta):
                    for attr_name, attr_value in base.__dict__.items():
                        if callable(attr_value) and not attr_name.startswith('__'):
                            if attr_name not in namespace:
                                raise TypeError(
                                    f"Class '{name}' must implement method '{attr_name}' of interface '{base.__name__}'"
                                )
                            
                            base_signature = inspect.signature(attr_value)
                            derived_signature = inspect.signature(namespace[attr_name])
                            
                            if base_signature != derived_signature:
                                raise TypeError(
                                    f"Method '{attr_name}' in class '{name}' does not match the interface signature.\n"
                                    f"Expected: {base_signature}\n"
                                    f"Got: {derived_signature}"
                                )
        return super().__new__(mcs, name, bases, namespace)

class Interface(metaclass=InterfaceMeta):
    """Base class for all interfaces."""
    pass

Example Usage

class MyInterface(Interface):
    def method(self, x: int) -> str:
        pass

class ValidImplementation(MyInterface):
    def method(self, x: int) -> str:
        return str(x)

class InvalidImplementation(MyInterface):
    def method(self, x):  # Missing return annotation
        return str(x)

# Raises TypeError: "Method 'method' in class 'InvalidImplementation' does not match the interface signature."
# Expected: (self, x: int) -> str
# Got: (self, x)

Performance Impact
Minimal, as the verification occurs only at class definition time.

I created a project on Pypi with this code for personal use.I leave it here in case you want to see it: Client Challenge

What do you mean by “runtime error”? Are you referring to abstractmethod?

Type checkers like mypy or pyright will enforce that the method in a subclass must not have incompatible types but by default they allow a function or method to omit type annotations. You can use e.g. mypy --strict or # pyright: strict to enforce that all functions/methods should have type annotations and then a static type checker will verify that all subclasses have consistent type signatures for the given method. A subclass can still widen the parameter types and narrow the return types though.

2 Likes