#!/usr/bin/env python## __COPYRIGHT__## Count statistics about SCons test and source files. This must be run# against a fully-populated tree (for example, one that's been freshly# checked out).## A test file is anything under the src/ directory that begins with# 'test_' or ends in 'Tests.py', or anything under the test/ directory# that ends in '.py'. Note that runtest.py script does *not*, by default,# consider the files that begin with 'test_' to be tests, because they're# tests of SCons packaging and installation, not functional tests of# SCons code.## A source file is anything under the src/engine/ or src/script/# directories that ends in '.py' but does NOT begin with 'test_'# or end in 'Tests.py'.## We report the number of tests and sources, the total number of lines# in each category, the number of non-blank lines, and the number of# non-comment lines. The last figure (non-comment) lines is the most# interesting one for most purposes.from__future__importdivision__revision__="__FILE__ __REVISION__ __DATE__ __DEVELOPER__"importos.pathfmt="%-16s%5s%7s%9s%11s%11s"classCollection(object):def__init__(self,name,files=None,pred=None):self._name=nameiffilesisNone:files=[]self.files=filesifpredisNone:pred=lambdax:Trueself.pred=preddef__call__(self,fname):returnself.pred(fname)def__len__(self):returnlen(self.files)defcollect(self,directory):fordirpath,dirnames,filenamesinos.walk(directory):try:dirnames.remove('.svn')exceptValueError:passself.files.extend([os.path.join(dirpath,f)forfinfilenamesifself.pred(f)])deflines(self):try:returnself._linesexceptAttributeError:self._lines=lines=[]forfileinself.files:file_lines=open(file).readlines()lines.extend([s.lstrip()forsinfile_lines])returnlinesdefnon_blank(self):return[sforsinself.lines()ifs!='']defnon_comment(self):return[sforsinself.lines()ifs==''ors[0]!='#']defnon_blank_non_comment(self):return[sforsinself.lines()ifs!=''ands[0]!='#']defprintables(self):return(self._name+':',len(self.files),len(self.lines()),len(self.non_blank()),len(self.non_comment()),len(self.non_blank_non_comment()))defis_Tests_py(x):returnx[-8:]=='Tests.py'defis_test_(x):returnx[:5]=='test_'defis_python(x):returnx[-3:]=='.py'defis_source(x):returnis_python(x)andnotis_Tests_py(x)andnotis_test_(x)src_Tests_py_tests=Collection('src/ *Tests.py',pred=is_Tests_py)src_test_tests=Collection('src/ test_*.py',pred=is_test_)test_tests=Collection('test/ tests',pred=is_python)sources=Collection('sources',pred=is_source)src_Tests_py_tests.collect('src')src_test_tests.collect('src')test_tests.collect('test')sources.collect('src/engine')sources.collect('src/script')src_tests=Collection('src/ tests',src_Tests_py_tests.files+src_test_tests.files)all_tests=Collection('all tests',src_tests.files+test_tests.files)defratio(over,under):return"%.2f"%(float(len(over))/float(len(under)))printfmt%('','','','','','non-blank')printfmt%('','files','lines','non-blank','non-comment','non-comment')printprintfmt%src_Tests_py_tests.printables()printfmt%src_test_tests.printables()printprintfmt%src_tests.printables()printfmt%test_tests.printables()printprintfmt%all_tests.printables()printfmt%sources.printables()printprintfmt%('ratio:',ratio(all_tests,sources),ratio(all_tests.lines(),sources.lines()),ratio(all_tests.non_blank(),sources.non_blank()),ratio(all_tests.non_comment(),sources.non_comment()),ratio(all_tests.non_blank_non_comment(),sources.non_blank_non_comment()))