2 # To check a particular Tahoe source distribution, this should be invoked from
3 # the root directory of that distribution as
5 # bin/tahoe @misc/coding_tools/check-interfaces.py
9 import zope.interface as zi
10 from zope.interface.verify import verifyClass
11 from zope.interface.advice import addClassAdvisor
14 interesting_modules = re.compile(r'(allmydata)|(foolscap)\..*')
15 excluded_classnames = re.compile(r'(_)|(Mock)|(Fake)|(Dummy).*')
16 excluded_file_basenames = re.compile(r'(check)|(bench)_.*')
19 other_modules_with_violations = set()
22 def strictly_implements(*interfaces):
23 frame = sys._getframe(1)
24 f_locals = frame.f_locals
26 # Try to make sure we were called from a class def. Assumes Python > 2.2.
27 if f_locals is frame.f_globals or '__module__' not in f_locals:
28 raise TypeError("implements can be used only from a class definition.")
30 if '__implements_advice_data__' in f_locals:
31 raise TypeError("implements can be used only once in a class definition.")
33 def _implements_advice(cls):
34 interfaces, classImplements = cls.__dict__['__implements_advice_data__']
35 del cls.__implements_advice_data__
36 classImplements(cls, *interfaces)
38 if interesting_modules.match(cls.__module__):
39 if not excluded_classnames.match(cls.__name__):
40 for interface in interfaces:
42 verifyClass(interface, cls)
44 print >>sys.stderr, ("%s.%s does not implement %s.%s:\n%s"
45 % (cls.__module__, cls.__name__,
46 interface.__module__, interface.__name__, e))
48 other_modules_with_violations.add(cls.__module__)
51 f_locals['__implements_advice_data__'] = interfaces, zi.classImplements
52 addClassAdvisor(_implements_advice, depth=2)
56 zi.implements = strictly_implements
59 # attempt to avoid side-effects from importing command scripts
60 sys.argv = ['', '--help']
63 # import modules under src/
65 for (dirpath, dirnames, filenames) in os.walk(srcdir):
67 (basename, ext) = os.path.splitext(fn)
68 if ext == '.py' and not excluded_file_basenames.match(basename):
69 relpath = os.path.join(dirpath[len(srcdir)+1:], basename)
70 module = relpath.replace(os.sep, '/').replace('/', '.')
78 others = list(other_modules_with_violations)
80 print >>sys.stderr, "There were also interface violations in:\n", ", ".join(others), "\n"