Skip to content

Instantly share code, notes, and snippets.

@alecthomas
Last active December 20, 2015 04:59
Show Gist options
  • Save alecthomas/6074934 to your computer and use it in GitHub Desktop.
Save alecthomas/6074934 to your computer and use it in GitHub Desktop.
An ABCMeta that validates the method signatures of concrete implementations.
import inspect
from abc import ABCMeta
class SignatureValidatingABCMeta(ABCMeta):
"""An ABCMeta that validates the method signatures of concrete implementations.
That is, given:
class Class(object):
__metaclass__ = SignatureValidatingABCMeta
@abstractmethod
def method(self, a, b):
pass
The following implementation will raise a TypeError at import:
class Subclass(Class):
def method(self, a): # TypeError: Signature of Subclass.method(a) differs from @abstractmethod Class.method(a, b)
pass
"""
def __new__(mcls, name, bases, namespace):
cls = super(SignatureValidatingABCMeta, mcls).__new__(mcls, name, bases, namespace)
signatures = set()
all_abstract = set()
for scls in cls.__mro__:
scls_abstract = set(getattr(scls, '__abstractmethods__', []))
for name in scls_abstract - all_abstract:
signatures.add((name, getattr(scls, name)))
all_abstract.update(scls_abstract)
for name, signature in signatures:
method = getattr(cls, name)
if hasattr(method, '__isabstractmethod__'):
continue
expected = inspect.getargspec(signature)
have = inspect.getargspec(method)
if expected != have:
raise TypeError('Signature of %s.%s%s differs from @abstractmethod %s.%s%s'
% (method.im_class.__name__, method.__name__, inspect.formatargspec(*have),
signature.im_class.__name__, signature.__name__, inspect.formatargspec(*expected)))
return cls
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment