# To check a particular Tahoe source distribution, this should be invoked from # the root directory of that distribution as # # bin/tahoe @misc/coding_tools/check-interfaces.py import os, sys, re import zope.interface as zi from zope.interface.verify import verifyClass from zope.interface.advice import addClassAdvisor interesting_modules = re.compile(r'(allmydata)|(foolscap)\..*') excluded_classnames = re.compile(r'(_)|(Mock)|(Fake).*') excluded_file_basenames = re.compile(r'check_.*') other_modules_with_violations = set() # deep magic def strictly_implements(*interfaces): frame = sys._getframe(1) f_locals = frame.f_locals # Try to make sure we were called from a class def. Assumes Python > 2.2. if f_locals is frame.f_globals or '__module__' not in f_locals: raise TypeError("implements can be used only from a class definition.") if '__implements_advice_data__' in f_locals: raise TypeError("implements can be used only once in a class definition.") def _implements_advice(cls): interfaces, classImplements = cls.__dict__['__implements_advice_data__'] del cls.__implements_advice_data__ classImplements(cls, *interfaces) if interesting_modules.match(cls.__module__): if not excluded_classnames.match(cls.__name__): for interface in interfaces: try: verifyClass(interface, cls) except Exception, e: print >>sys.stderr, ("%s.%s does not implement %s.%s:\n%s" % (cls.__module__, cls.__name__, interface.__module__, interface.__name__, e)) else: other_modules_with_violations.add(cls.__module__) return cls f_locals['__implements_advice_data__'] = interfaces, zi.classImplements addClassAdvisor(_implements_advice, depth=2) # patchee-monkey zi.implements = strictly_implements # attempt to avoid side-effects from importing command scripts sys.argv = ['', '--help'] from twisted.python.filepath import FilePath # import modules under src/ src = FilePath('src') for fp in src.walk(): (basepath, ext) = fp.splitext() if ext == '.py' and not excluded_file_basenames.match(fp.basename()): relpath = os.path.relpath(basepath, src.path) module = relpath.replace(os.path.sep, '/').replace('/', '.') try: __import__(module) except ImportError: import traceback traceback.print_exc() print >>sys.stderr others = list(other_modules_with_violations) others.sort() print >>sys.stderr, "There were also interface violations in:\n", ", ".join(others), "\n"