Checks a class definition for required attributes <br> To use it, add two lines to your class, __metaclass__=InterfaceChecker and __implements__=[InterfaceName]. The example below generates the following error message: <br> InterfaceOmission: ['__delitem__'] <br> Verifying interfaces for an object becomes trivial. For instance, if you need to validate that variable 'x' implements a minimal sequence interface, verify that: <br> MinimalSequence in x.__implements__
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
########## First define the interface checker and interfaces from sets import Set import UserDict class InterfaceOmission(Exception): pass class InterfaceChecker(type): "Validates the presence of required attributes" def __new__(cls, classname, bases, classdict): obj = type(classname, bases, classdict) if '__implements__' in classdict: defined = Set(dir(obj)) for interface in classdict['__implements__']: required = Set(dir(interface)) if not required.issubset(defined): raise InterfaceOmission, list(required - defined) return obj class MinimalMapping(object): "Interface specification" def __getitem__(self, key): pass def __setitem__(self, key, value): pass def __delitem__(self, key): pass def __contains__(self, key): pass class FullMapping(MinimalMapping, UserDict.DictMixin): pass class MinimalSequence(object): def __len__(self): pass def __getitem__(self, index): pass class Callable(object): def __call__(self, *args): pass ########## The user code starts here class MyClass(object): __metaclass__ = InterfaceChecker __implements__ = [MinimalMapping, Callable] def __getitem__(self, key): pass def __setitem__(self, key, value): pass #def __delitem__(self, key): pass def __contains__(self, key): pass def __call__(self, *args): pass def setdefault(self, key, default): 'Not required by the interface' m = MyClass() assert MinimalMapping in m.__implements__
Fancier implementations are possible. The above is mostly a proof-of-concept.
The sets.Set class from Python 2.3 is used to make the code more clear. An equivalent using dictionaries is easily coded for Python 2.2. Also, UserDict.DictMixin, a Py 2.3 feature, is used in the definition of a full mapping interface; however, that is easily coded in Python 2.2 (see the cookbook recipe for the code).
I like it for the nice usage of the set module. The only change I would do is in the variable names in InterfaceChecker.__new__:
cls -> mcl, obj -> cls.
They look nicer to me ;)