def validate(obj, classes=None): """ Validate the object against the classes. This is like isinstance, except that it validates that the object has implemented all the abstract methods/properties of all the classes. """ if classes is None: classes = obj.mro() #classes.extend(obj.__implements__) if not isinstance(classes, (list, tuple)): classes = (classes,) abstracts = set() for cls in classes: if not isinstance(cls, type): raise TypeError("Can only validate against classes") for name in getattr(cls, "__abstractmethods__", set()): value = getattr(obj, name, None) if not value: abstracts.add(name) elif getattr(value, "__isabstractmethod__", False): abstracts.add(name) if abstracts: sorted_methods = sorted(abstracts) joined = ", ".join(sorted_methods) try: name = obj.__name__ except AttributeError: name = obj msg = "{} does not implement abstract methods {}" raise TypeError(msg.format(name, joined)) def conforms(classes): """A class decorator factory for validating against an ABC.""" def decorator(cls): if not __debug__: return cls if not isinstance(cls, type): raise TypeError("Can only validate classes") validate(cls, classes) return cls return decorator