tahoe-lafs/misc/coding_tools/check-interfaces.py

82 lines
2.7 KiB
Python

# 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"