Last active
December 20, 2015 04:59
-
-
Save alecthomas/6074934 to your computer and use it in GitHub Desktop.
An ABCMeta that validates the method signatures of concrete implementations.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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