]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - misc/coding_tools/check-interfaces.py
c5dbf246992b6a29903b4b3850703bcdbc447ed7
[tahoe-lafs/tahoe-lafs.git] / misc / coding_tools / check-interfaces.py
1
2 # To check a particular Tahoe source distribution, this should be invoked from
3 # the root directory of that distribution as
4 #
5 #   bin/tahoe @misc/coding_tools/check-interfaces.py
6
7 import os, sys, re
8
9 import zope.interface as zi
10 from zope.interface.verify import verifyClass
11 from zope.interface.advice import addClassAdvisor
12
13
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)_.*')
17
18
19 other_modules_with_violations = set()
20
21 # deep magic
22 def strictly_implements(*interfaces):
23     frame = sys._getframe(1)
24     f_locals = frame.f_locals
25
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.")
29
30     if '__implements_advice_data__' in f_locals:
31         raise TypeError("implements can be used only once in a class definition.")
32
33     def _implements_advice(cls):
34         interfaces, classImplements = cls.__dict__['__implements_advice_data__']
35         del cls.__implements_advice_data__
36         classImplements(cls, *interfaces)
37
38         if interesting_modules.match(cls.__module__):
39             if not excluded_classnames.match(cls.__name__):
40                 for interface in interfaces:
41                     try:
42                         verifyClass(interface, cls)
43                     except Exception, e:
44                         print >>sys.stderr, ("%s.%s does not implement %s.%s:\n%s"
45                                              % (cls.__module__, cls.__name__,
46                                                 interface.__module__, interface.__name__, e))
47         else:
48             other_modules_with_violations.add(cls.__module__)
49         return cls
50
51     f_locals['__implements_advice_data__'] = interfaces, zi.classImplements
52     addClassAdvisor(_implements_advice, depth=2)
53
54
55 # patchee-monkey
56 zi.implements = strictly_implements
57
58
59 # attempt to avoid side-effects from importing command scripts
60 sys.argv = ['', '--help']
61
62
63 from twisted.python.filepath import FilePath
64
65 # import modules under src/
66 src = FilePath('src')
67 for fp in src.walk():
68     (basepath, ext) = fp.splitext()
69     if ext == '.py' and not excluded_file_basenames.match(fp.basename()):
70         relpath = os.path.relpath(basepath, src.path)
71         module = relpath.replace(os.path.sep, '/').replace('/', '.')
72         try:
73             __import__(module)
74         except ImportError:
75             import traceback
76             traceback.print_exc()
77             print >>sys.stderr
78
79 others = list(other_modules_with_violations)
80 others.sort()
81 print >>sys.stderr, "There were also interface violations in:\n", ", ".join(others), "\n"