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 ;)