From: david-sarah Date: Thu, 15 Sep 2011 16:15:32 +0000 (-0700) Subject: Add a script 'misc/coding_tools/check-interfaces.py' that checks whether zope interfa... X-Git-Tag: allmydata-tahoe-1.9.0a2~14 X-Git-Url: https://git.rkrishnan.org/pf/content/en/footer/rgr-080307.php?a=commitdiff_plain;h=417054aabad6cf4b891fd7909fd36a086dd8a0a8;p=tahoe-lafs%2Ftahoe-lafs.git Add a script 'misc/coding_tools/check-interfaces.py' that checks whether zope interfaces are enforced. Also add 'check-interfaces', 'version-and-path', and 'code-checks' targets to the Makefile. fixes #1474 --- diff --git a/Makefile b/Makefile index 47492e2d..e5a8121a 100644 --- a/Makefile +++ b/Makefile @@ -121,12 +121,26 @@ upload-coverage: false endif +code-checks: build version-and-path check-interfaces -find-trailing-spaces -check-umids pyflakes + +version-and-path: + $(TAHOE) --version-and-path + +check-interfaces: + $(TAHOE) @misc/coding_tools/check-interfaces.py 2>&1 |tee violations.txt + @echo pyflakes: $(PYTHON) -OOu `which pyflakes` $(SOURCES) |sort |uniq + @echo check-umids: $(PYTHON) misc/coding_tools/check-umids.py `find $(SOURCES) -name '*.py'` + @echo + +-check-umids: + -$(PYTHON) misc/coding_tools/check-umids.py `find $(SOURCES) -name '*.py'` + @echo count-lines: @echo -n "files: " @@ -213,6 +227,11 @@ clean: find-trailing-spaces: $(PYTHON) misc/coding_tools/find-trailing-spaces.py -r $(SOURCES) + @echo + +-find-trailing-spaces: + -$(PYTHON) misc/coding_tools/find-trailing-spaces.py -r $(SOURCES) + @echo # The test-desert-island target grabs the tahoe-deps tarball, unpacks it, # does a build, then asserts that the build did not try to download anything diff --git a/misc/coding_tools/check-interfaces.py b/misc/coding_tools/check-interfaces.py new file mode 100644 index 00000000..2a22cffc --- /dev/null +++ b/misc/coding_tools/check-interfaces.py @@ -0,0 +1,81 @@ + +# 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"