Recipe20.9.Checking Whether Interfaces Are Implemented


Recipe 20.9. Checking Whether Interfaces Are Implemented

Credit: Raymond Hettinger

Problem

You want to ensure that the classes you define implement the interfaces that they claim to implement.

Solution

Python does not have a formal concept of "interface", but we can easily represent interfaces by means of "skeleton" classes such as:

class IMinimalMapping(object):     def _ _getitem_ _(self, key): pass     def _ _setitem_ _(self, key, value): pass     def _ _delitem_ _(self, key): pass     def _ _contains_ _(self, key): pass import UserDict class IFullMapping(IMinimalMapping, UserDict.DictMixin):     def keys(self): pass class IMinimalSequence(object):     def _ _len_ _(self): pass     def _ _getitem_ _(self, index): pass class ICallable(object):     def _ _call_ _(self, *args): pass

We follow the natural convention that any class can represent an interface: the interface is the set of methods and other attributes of the class. We can say that a class C implements an interface i if C has all the methods and other attributes of i (and, possibly, additional ones).

We can now define a simple custom metaclass that checks whether classes implement all the interfaces they claim to implement:

# ensure we use the best available 'set' type with name 'set' try:     set except NameError:     from sets import Set as set # a custom exception class that we raise to signal violations class InterfaceOmission(TypeError):     pass class MetaInterfaceChecker(type):     ''' the interface-checking custom metaclass '''     def _ _init_ _(cls, classname, bases, classdict):         super(MetaInterfaceChecker, cls)._ _init_ _(classname, bases, classdict)         cls_defines = set(dir(cls))         for interface in cls._ _implements_ _:             itf_requires = set(dir(interface))             if not itf_requires.issubset(cls_defines):                 raise InterfaceOmission, list(itf_requires - cls_defines)

Any class that uses MetaInterfaceChecker as its metaclass must expose a class attribute _ _implements_ _, an iterable whose items are the interfaces the class claims to implement. The metaclass checks the claim, raising an InterfaceOmission exception if the claim is false.

Discussion

Here's an example class using the MetaInterfaceChecker custom metaclass:

class Skidoo(object):     ''' a mapping which claims to contain all keys, each with a value         of 23; item setting and deletion are no-ops; you can also call         an instance with arbitrary positional args, result is 23. '''     _ _metaclass_ _ = MetaInterfaceChecker     _ _implements_ _ = IMinimalMapping, ICallable     def _ _getitem_ _(self, key): return 23     def _ _setitem_ _(self, key, value): pass     def _ _delitem_ _(self, key): pass     def _ _contains_ _(self, key): return True     def _ _call_ _(self, *args): return 23 sk = Skidoo( )

Any code dealing with an instance of such a class can choose to check whether it can rely on certain interfaces:

def use(sk):     if IMinimalMapping in sk._ _implements_ _:     ...code using 'sk[...]' and/or 'x in sk'...

You can, if you want, provide much fancier and more thorough checks, for example by using functions from standard library module inspect to check that the attributes being exposed and required are methods with compatible signatures. However, this simple recipe does show how to automate the simplest kind of checks for interface compliance.

See Also

Library Reference and Python in a Nutshell docs about module sets, (in Python 2.4 only) the set built-in, custom metaclasses, the inspect module.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net