ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/WMCORE/setup.py
Revision: 1.93
Committed: Wed Feb 3 12:50:42 2010 UTC (15 years, 2 months ago) by metson
Content type: text/x-python
Branch: MAIN
Changes since 1.92: +4 -2 lines
Log Message:
Correctly print the env if setup is called from another directory

File Contents

# User Rev Content
1 fvlingen 1.1 #!/usr/bin/env python
2 metson 1.21 from distutils.core import setup, Command
3     from unittest import TextTestRunner, TestLoader, TestSuite
4     from glob import glob
5     from os.path import splitext, basename, join as pjoin, walk
6 metson 1.85 from ConfigParser import ConfigParser, NoOptionError
7 meloam 1.53 import os, sys, os.path
8 metson 1.85 import logging
9     import unittest
10 metson 1.51 #PyLinter and coverage aren't standard, but aren't strictly necessary
11     can_lint = False
12     can_coverage = False
13 meloam 1.88 can_nose = False
14 sfoulkes 1.29 try:
15 metson 1.87 from pylint.lint import Run
16 metson 1.85 from pylint.lint import preprocess_options, cb_init_hook
17     from pylint import checkers
18 metson 1.51 can_lint = True
19     except:
20     pass
21     try:
22 meloam 1.47 import coverage
23 metson 1.51 can_coverage = True
24 sfoulkes 1.29 except:
25     pass
26 fvlingen 1.1
27 meloam 1.88 try:
28     import nose
29     can_nose = True
30     except:
31     pass
32    
33     if can_nose:
34     class NoseCommand(Command):
35     user_options = [ ]
36    
37     def initialize_options(self):
38     pass
39    
40     def finalize_options(self):
41     pass
42    
43     def run(self):
44 meloam 1.90 retval = nose.run(argv=[__file__,'--all-modules','-v','test/python'])
45     print "nose returned: %s" % retval
46 meloam 1.91 return retval
47 meloam 1.88 else:
48     class NoseCommand(Command):
49     user_options = [ ]
50     def run(self):
51     print "Nose isn't installed, fail"
52     pass
53    
54     def initialize_options(self):
55     pass
56    
57     def finalize_options(self):
58     pass
59     pass
60    
61 metson 1.87 if can_lint:
62     class LinterRun(Run):
63     def __init__(self, args, reporter=None):
64     self._rcfile = None
65     self._plugins = []
66     preprocess_options(args, {
67     # option: (callback, takearg)
68     'rcfile': (self.cb_set_rcfile, True),
69     'load-plugins': (self.cb_add_plugins, True),
70     })
71     self.linter = linter = self.LinterClass((
72     ('rcfile',
73     {'action' : 'callback', 'callback' : lambda *args: 1,
74     'type': 'string', 'metavar': '<file>',
75     'help' : 'Specify a configuration file.'}),
76    
77     ('init-hook',
78     {'action' : 'callback', 'type' : 'string', 'metavar': '<code>',
79     'callback' : cb_init_hook,
80     'help' : 'Python code to execute, usually for sys.path \
81     manipulation such as pygtk.require().'}),
82    
83     ('help-msg',
84     {'action' : 'callback', 'type' : 'string', 'metavar': '<msg-id>',
85     'callback' : self.cb_help_message,
86     'group': 'Commands',
87     'help' : '''Display a help message for the given message id and \
88     exit. The value may be a comma separated list of message ids.'''}),
89    
90     ('list-msgs',
91     {'action' : 'callback', 'metavar': '<msg-id>',
92     'callback' : self.cb_list_messages,
93     'group': 'Commands',
94     'help' : "Generate pylint's full documentation."}),
95    
96     ('generate-rcfile',
97     {'action' : 'callback', 'callback' : self.cb_generate_config,
98     'group': 'Commands',
99     'help' : '''Generate a sample configuration file according to \
100     the current configuration. You can put other options before this one to get \
101     them in the generated configuration.'''}),
102    
103     ('generate-man',
104     {'action' : 'callback', 'callback' : self.cb_generate_manpage,
105     'group': 'Commands',
106     'help' : "Generate pylint's man page.",'hide': 'True'}),
107    
108     ('errors-only',
109     {'action' : 'callback', 'callback' : self.cb_error_mode,
110     'short': 'e',
111     'help' : '''In error mode, checkers without error messages are \
112     disabled and for others, only the ERROR messages are displayed, and no reports \
113     are done by default'''}),
114    
115     ('profile',
116     {'type' : 'yn', 'metavar' : '<y_or_n>',
117     'default': False,
118     'help' : 'Profiled execution.'}),
119    
120     ), option_groups=self.option_groups,
121     reporter=reporter, pylintrc=self._rcfile)
122     # register standard checkers
123     checkers.initialize(linter)
124     # load command line plugins
125     linter.load_plugin_modules(self._plugins)
126     # read configuration
127     linter.disable_message('W0704')
128     linter.read_config_file()
129     # is there some additional plugins in the file configuration, in
130     config_parser = linter._config_parser
131     if config_parser.has_option('MASTER', 'load-plugins'):
132     plugins = splitstrip(config_parser.get('MASTER', 'load-plugins'))
133     linter.load_plugin_modules(plugins)
134     # now we can load file config and command line, plugins (which can
135     # provide options) have been registered
136     linter.load_config_file()
137     if reporter:
138     # if a custom reporter is provided as argument, it may be overriden
139     # by file parameters, so re-set it here, but before command line
140     # parsing so it's still overrideable by command line option
141     linter.set_reporter(reporter)
142     args = linter.load_command_line_configuration(args)
143     # insert current working directory to the python path to have a correct
144     # behaviour
145     sys.path.insert(0, os.getcwd())
146     if self.linter.config.profile:
147     print >> sys.stderr, '** profiled run'
148     from hotshot import Profile, stats
149     prof = Profile('stones.prof')
150     prof.runcall(linter.check, args)
151     prof.close()
152     data = stats.load('stones.prof')
153     data.strip_dirs()
154     data.sort_stats('time', 'calls')
155     data.print_stats(30)
156     sys.path.pop(0)
157    
158     def cb_set_rcfile(self, name, value):
159     """callback for option preprocessing (ie before optik parsing)"""
160     self._rcfile = value
161    
162     def cb_add_plugins(self, name, value):
163     """callback for option preprocessing (ie before optik parsing)"""
164     self._plugins.extend(splitstrip(value))
165    
166     def cb_error_mode(self, *args, **kwargs):
167     """error mode:
168     * checkers without error messages are disabled
169     * for others, only the ERROR messages are displayed
170     * disable reports
171     * do not save execution information
172     """
173     self.linter.disable_noerror_checkers()
174     self.linter.set_option('disable-msg-cat', 'WCRFI')
175     self.linter.set_option('reports', False)
176     self.linter.set_option('persistent', False)
177    
178     def cb_generate_config(self, *args, **kwargs):
179     """optik callback for sample config file generation"""
180     self.linter.generate_config(skipsections=('COMMANDS',))
181    
182     def cb_generate_manpage(self, *args, **kwargs):
183     """optik callback for sample config file generation"""
184     from pylint import __pkginfo__
185     self.linter.generate_manpage(__pkginfo__)
186    
187     def cb_help_message(self, option, opt_name, value, parser):
188     """optik callback for printing some help about a particular message"""
189     self.linter.help_message(splitstrip(value))
190    
191     def cb_list_messages(self, option, opt_name, value, parser):
192     """optik callback for printing available messages"""
193     self.linter.list_messages()
194     else:
195     class LinterRun:
196     def __init__(self):
197     pass
198 metson 1.85
199 metson 1.22 """
200     Build, clean and test the WMCore package.
201     """
202    
203 metson 1.92 def generate_filelist(basepath=None, recurse=True, ignore=False):
204 metson 1.85 if basepath:
205     walkpath = os.path.join(get_relative_path(), 'src/python', basepath)
206     else:
207     walkpath = os.path.join(get_relative_path(), 'src/python')
208    
209 metson 1.36 files = []
210 metson 1.85
211     if walkpath.endswith('.py'):
212 metson 1.92 if ignore and walkpath.endswith(ignore):
213     files.append(walkpath)
214 metson 1.85 else:
215     for dirpath, dirnames, filenames in os.walk(walkpath):
216     # skipping CVS directories and their contents
217     pathelements = dirpath.split('/')
218     result = []
219     if not 'CVS' in pathelements:
220     # to build up a list of file names which contain tests
221     for file in filenames:
222     if file.endswith('.py'):
223     filepath = '/'.join([dirpath, file])
224     files.append(filepath)
225    
226     if len(files) == 0 and recurse:
227     files = generate_filelist(basepath + '.py', not recurse)
228    
229 metson 1.36 return files
230    
231 metson 1.85 def lint_score(stats, evaluation):
232     return eval(evaluation, {}, stats)
233    
234     def lint_files(files, reports=False):
235 metson 1.36 """
236 metson 1.85 lint a (list of) file(s) and return the results as a dictionary containing
237     filename : result_dict
238 metson 1.36 """
239 metson 1.85
240     rcfile=os.path.join(get_relative_path(),'standards/.pylintrc')
241    
242 metson 1.92 arguements = ['--rcfile=%s' % rcfile, '--ignore=DefaultConfig.py']
243 metson 1.86
244     if not reports:
245     arguements.append('-rn')
246    
247 metson 1.85 arguements.extend(files)
248    
249     lntr = LinterRun(arguements)
250    
251     results = {}
252     for file in files:
253     lntr.linter.check(file)
254     results[file] = {'stats': lntr.linter.stats,
255     'score': lint_score(lntr.linter.stats,
256     lntr.linter.config.evaluation)
257     }
258     if reports:
259     print '----------------------------------'
260     print 'Your code has been rated at %.2f/10' % \
261     lint_score(lntr.linter.stats, lntr.linter.config.evaluation)
262 metson 1.36
263 metson 1.85 return results, lntr.linter.config.evaluation
264 meloam 1.69
265    
266    
267 meloam 1.70 MODULE_EXTENSIONS = set('.py'.split())
268     ## bad bad bad global variable, FIXME
269     all_test_suites = []
270 meloam 1.69
271 meloam 1.72
272 meloam 1.69 def get_test_suites(path):
273     """:return: Iterable of suites for the packages/modules
274     present under :param:`path`.
275     """
276 meloam 1.70 logging.info('Base path: %s', path)
277 meloam 1.69 suites = []
278 meloam 1.70 os.path.walk(path, unit_test_extractor, (path, suites))
279     logging.info('Got suites: %s', all_test_suites)
280     return all_test_suites
281 meloam 1.69
282 meloam 1.76 import re
283     def listFiles(dir):
284     fileList = []
285     basedir = dir
286     for item in os.listdir(dir):
287     if os.path.isfile(os.path.join(basedir,item)):
288     fileList.append(os.path.join(basedir,item))
289     elif os.path.isdir(os.path.join(basedir,item)):
290     fileList.extend(listFiles(os.path.join(basedir,item)))
291    
292     return fileList
293    
294    
295     import pprint
296 meloam 1.69
297 meloam 1.47 def runUnitTests():
298     # runs all the unittests, returning the testresults object
299     testfiles = []
300     # Add the test and src directory to the python path
301     mydir = os.getcwd()
302     # todo: not portable
303 meloam 1.57 testspypath = '/'.join([mydir, 'test/python/'])
304     srcpypath = '/'.join([mydir, 'src/python/'])
305 meloam 1.47 sys.path.append(testspypath)
306     sys.path.append(srcpypath)
307 meloam 1.70 logging.basicConfig(level=logging.DEBUG)
308 meloam 1.76 path = os.path.abspath(os.path.dirname(sys.argv[0]))
309     files = listFiles(path)
310     test = re.compile("_t\.py$", re.IGNORECASE)
311     files = filter(test.search, files)
312     filenameToModuleName = lambda f: os.path.splitext(f)[0]
313     moduleNames = map(filenameToModuleName, files)
314     stripBeginning = lambda f: f[ len(path) + len('/test/python/'): ]
315     moduleNames2 = map( stripBeginning, moduleNames )
316     replaceSlashes = lambda f: f.replace('/','.')
317     moduleNames3 = map( replaceSlashes, moduleNames2 )
318 meloam 1.80 modules = []
319     loadFail = []
320 meloam 1.76 for oneModule in moduleNames3:
321     try:
322 meloam 1.78 __import__(oneModule)
323     modules.append(sys.modules[oneModule])
324 meloam 1.79 except Exception, e:
325 meloam 1.84 sys.stderr.write("ERROR: Can't load test case %s - %s\n" % (oneModule, e))
326 meloam 1.80 loadFail.append(oneModule)
327    
328 meloam 1.76 load = unittest.defaultTestLoader.loadTestsFromModule
329     globalSuite = unittest.TestSuite(map(load, modules))
330     # # logging.basicConfig(level=logging.WARN)
331     # package_path = os.path.dirname(mydir + '/test/python/')
332     # print "path: %s " % package_path
333     # suites = get_test_suites(package_path)
334     # testCaseCount = 0
335     # totallySuite = unittest.TestSuite()
336     # totallySuite.addTests(suites)
337     # print suites
338     result = unittest.TextTestRunner(verbosity=2).run(globalSuite)
339 meloam 1.70
340 meloam 1.59 #sys.stdout = sys.__stdout__
341     #sys.stderr = sys.__stderr__
342 meloam 1.57
343     print sys.path
344 meloam 1.81 return (result, loadFail, globalSuite.countTestCases())
345 meloam 1.47
346 metson 1.85 def get_relative_path():
347     return os.path.dirname(os.path.abspath(os.path.join(os.getcwd(), sys.argv[0])))
348    
349 meloam 1.47
350 metson 1.21 class TestCommand(Command):
351 metson 1.49 description = "Handle setup.py test with this class - walk through the " + \
352     "directory structure building up a list of tests, then build a test " + \
353     " suite and execute it."
354 metson 1.22 """
355     TODO: Pull database URL's from environment, and skip tests where database
356     URL is not present (e.g. for a slave without Oracle connection)
357 meloam 1.46
358     TODO: need to build a separate test runner for each test file, python is
359     keeping the test objects around, which is keeping it from destroying
360     filehandles, which is causing us to bomb out of a lot more tests than
361     necessary. Or, people could learn to close their files themselves.
362     either-or.
363 metson 1.22 """
364 metson 1.21 user_options = [ ]
365    
366     def initialize_options(self):
367 metson 1.85 self._dir = get_relative_path()
368 metson 1.21
369     def finalize_options(self):
370     pass
371    
372     def run(self):
373     '''
374 metson 1.25 Finds all the tests modules in test/python/WMCore_t, and runs them.
375 metson 1.21 '''
376     testfiles = [ ]
377 metson 1.26
378 meloam 1.48 # attempt to perform coverage tests, even if they weren't asked for
379     # it doesn't cost (much), and by caching the results, a later
380     # coverage test run doesn't have to run through all the unittests
381     # just generate the coverage report
382     files = generate_filelist()
383     coverageEnabled = True;
384     try:
385     cov = coverage.coverage(branch = True, data_file="wmcore-coverage.dat" )
386     cov.start()
387     print "Caching code coverage statstics"
388     except:
389     coverageEnabled = False
390    
391 meloam 1.75 ## FIXME: make this more portable
392     if 'WMCOREBASE' not in os.environ:
393 metson 1.85 os.environ['WMCOREBASE'] = get_relative_path()
394 meloam 1.75
395 meloam 1.54 result, failedTestFiles, totalTests = runUnitTests()
396 meloam 1.48
397     if coverageEnabled:
398     cov.stop()
399     cov.save()
400    
401 meloam 1.54
402 metson 1.32 if not result.wasSuccessful():
403 meloam 1.82 sys.stderr.write("\nTests unsuccessful. There were %s failures and %s errors\n"\
404 meloam 1.81 % (len(result.failures), len(result.errors)))
405     #print "Failurelist:\n%s" % "\n".join(map(lambda x: \
406     # "FAILURE: %s\n%s" % (x[0],x[1] ), result.failures))
407     #print "Errorlist:\n%s" % "\n".join(map(lambda x: \
408     # "ERROR: %s\n%s" % (x[0],x[1] ), result.errors))
409 meloam 1.54
410     if len(failedTestFiles):
411 meloam 1.81 sys.stderr.write("The following tests failed to load: \n===============\n%s" %\
412     "\n".join(failedTestFiles))
413 meloam 1.82 sys.stderr.write("\n------------------------------------")
414     sys.stderr.write("\ntest results")
415     sys.stderr.write("\n------------------------------------")
416     sys.stderr.write("\nStats: %s successful, %s failures, %s errors, %s didn't run" %\
417 meloam 1.54 (totalTests - len(result.failures) - len(result.errors),\
418     len(result.failures),
419     len(result.errors),
420 meloam 1.81 len(failedTestFiles)))
421 meloam 1.60
422 meloam 1.58 if (not result.wasSuccessful()) or len(result.errors):
423 meloam 1.82 sys.stderr.write("\nFAILED: setup.py test\n")
424 meloam 1.44 sys.exit(1)
425     else:
426 meloam 1.82 sys.stderr.write("\nPASS: setup.py test\n")
427 meloam 1.44 sys.exit(0)
428 meloam 1.54
429 meloam 1.44
430    
431    
432 metson 1.21 class CleanCommand(Command):
433 metson 1.49 description = "Clean up (delete) compiled files"
434 metson 1.21 user_options = [ ]
435    
436     def initialize_options(self):
437     self._clean_me = [ ]
438     for root, dirs, files in os.walk('.'):
439     for f in files:
440     if f.endswith('.pyc'):
441     self._clean_me.append(pjoin(root, f))
442    
443     def finalize_options(self):
444     pass
445    
446     def run(self):
447     for clean_me in self._clean_me:
448     try:
449     os.unlink(clean_me)
450     except:
451     pass
452    
453 metson 1.23 class LintCommand(Command):
454 metson 1.49 description = "Lint all files in the src tree"
455 metson 1.24 """
456     TODO: better format the test results, get some global result, make output
457     more buildbot friendly.
458     """
459    
460 metson 1.86 user_options = [ ('package=', 'p', 'package to lint, default to None'),
461     ('report', 'r', 'return a detailed lint report, default False')]
462 metson 1.23
463     def initialize_options(self):
464 metson 1.85 self._dir = get_relative_path()
465     self.package = None
466 metson 1.86 self.report = False
467 metson 1.85
468 metson 1.23 def finalize_options(self):
469 metson 1.86 if self.report:
470     self.report = True
471    
472 metson 1.23 def run(self):
473     '''
474     Find the code and run lint on it
475     '''
476 metson 1.51 if can_lint:
477 metson 1.85 srcpypath = os.path.join(self._dir, 'src/python/')
478    
479 metson 1.51 sys.path.append(srcpypath)
480    
481 metson 1.85 files_to_lint = []
482    
483     if self.package:
484     if self.package.endswith('.py'):
485     cnt = self.package.count('.') - 1
486 metson 1.92 files_to_lint = generate_filelist(self.package.replace('.', '/', cnt), 'DeafultConfig.py')
487 metson 1.85 else:
488 metson 1.92 files_to_lint = generate_filelist(self.package.replace('.', '/'), 'DeafultConfig.py')
489 metson 1.85 else:
490 metson 1.92 files_to_lint = generate_filelist(ignore='DeafultConfig.py')
491 metson 1.86
492     results, evaluation = lint_files(files_to_lint, self.report)
493 metson 1.85 ln = len(results)
494     scr = 0
495     print
496     for k, v in results.items():
497     print "%s: %.2f/10" % (k.replace('src/python/', ''), v['score'])
498     scr += v['score']
499     if ln > 1:
500     print '--------------------------------------------------------'
501     print 'Average pylint score for %s is: %.2f/10' % (self.package,
502     scr/ln)
503    
504 metson 1.51 else:
505     print 'You need to install pylint before using the lint command'
506 metson 1.36
507     class ReportCommand(Command):
508 metson 1.49 description = "Generate a simple html report for ease of viewing in buildbot"
509 metson 1.36 """
510     To contain:
511     average lint score
512     % code coverage
513     list of classes missing tests
514     etc.
515     """
516    
517     user_options = [ ]
518    
519     def initialize_options(self):
520     pass
521    
522     def finalize_options(self):
523     pass
524    
525     def run(self):
526     """
527     run all the tests needed to generate the report and make an
528     html table
529     """
530     files = generate_filelist()
531    
532     error = 0
533     warning = 0
534     refactor = 0
535     convention = 0
536     statement = 0
537    
538 metson 1.85 srcpypath = '/'.join([get_relative_path(), 'src/python/'])
539 metson 1.36 sys.path.append(srcpypath)
540 metson 1.37
541     cfg = ConfigParser()
542     cfg.read('standards/.pylintrc')
543    
544 metson 1.40 # Supress stdout/stderr
545 metson 1.38 sys.stderr = open('/dev/null', 'w')
546 metson 1.39 sys.stdout = open('/dev/null', 'w')
547 meloam 1.47 # wrap it in an exception handler, otherwise we can't see why it fails
548     try:
549     # lint the code
550     for stats in lint_files(files):
551     error += stats['error']
552     warning += stats['warning']
553     refactor += stats['refactor']
554     convention += stats['convention']
555     statement += stats['statement']
556     except Exception,e:
557     # and restore the stdout/stderr
558     sys.stderr = sys.__stderr__
559     sys.stdout = sys.__stderr__
560     raise e
561 metson 1.37
562 metson 1.40 # and restore the stdout/stderr
563 meloam 1.47 sys.stderr = sys.__stderr__
564     sys.stdout = sys.__stderr__
565 metson 1.38
566 metson 1.37 stats = {'error': error,
567     'warning': warning,
568     'refactor': refactor,
569     'convention': convention,
570     'statement': statement}
571    
572     lint_score = eval(cfg.get('MASTER', 'evaluation'), {}, stats)
573 metson 1.36 coverage = 0 # TODO: calculate this
574     testless_classes = [] # TODO: generate this
575    
576     print "<table>"
577     print "<tr>"
578     print "<td colspan=2><h1>WMCore test report</h1></td>"
579     print "</tr>"
580     print "<tr>"
581     print "<td>Average lint score</td>"
582     print "<td>%.2f</td>" % lint_score
583     print "</tr>"
584     print "<tr>"
585     print "<td>% code coverage</td>"
586     print "<td>%s</td>" % coverage
587     print "</tr>"
588     print "<tr>"
589     print "<td>Classes missing tests</td>"
590     print "<td>"
591     if len(testless_classes) == 0:
592     print "None"
593     else:
594     print "<ul>"
595     for c in testless_classes:
596     print "<li>%c</li>" % c
597     print "</ul>"
598     print "</td>"
599     print "</tr>"
600     print "</table>"
601    
602     class CoverageCommand(Command):
603 metson 1.49 description = "Run code coverage tests"
604 metson 1.36 """
605 metson 1.49 To do this, we need to run all the unittests within the coverage
606     framework to record all the lines(and branches) executed
607 meloam 1.47 unfortunately, we have multiple code paths per database schema, so
608     we need to find a way to merge them.
609    
610     TODO: modify the test command to have a flag to record code coverage
611     the file thats used can then be used here, saving us from running
612     our tests twice
613 metson 1.36 """
614    
615     user_options = [ ]
616    
617     def initialize_options(self):
618     pass
619    
620     def finalize_options(self):
621     pass
622    
623     def run(self):
624     """
625     Determine the code's test coverage and return that as a float
626    
627     http://nedbatchelder.com/code/coverage/
628     """
629 metson 1.51 if can_coverage:
630     files = generate_filelist()
631     dataFile = None
632     cov = None
633 meloam 1.48
634 metson 1.51 # attempt to load previously cached coverage information if it exists
635     try:
636     dataFile = open("wmcore-coverage.dat","r")
637     cov = coverage.coverage(branch = True, data_file='wmcore-coverage.dat')
638     cov.load()
639     except:
640     cov = coverage.coverage(branch = True, )
641     cov.start()
642     runUnitTests()
643     cov.stop()
644     cov.save()
645    
646     # we have our coverage information, now let's do something with it
647     # get a list of modules
648     cov.report(morfs = files, file=sys.stdout)
649     return 0
650     else:
651     print 'You need the coverage module installed before running the' +\
652     ' coverage command'
653 metson 1.36
654 metson 1.41 class DumbCoverageCommand(Command):
655 metson 1.49 description = "Run a simple coverage test - find classes that don't have a unit test"
656 metson 1.41
657     user_options = [ ]
658    
659     def initialize_options(self):
660     pass
661    
662     def finalize_options(self):
663     pass
664    
665     def run(self):
666     """
667     Determine the code's test coverage in a dumb way and return that as a
668     float.
669     """
670 metson 1.42 print "This determines test coverage in a very crude manner. If your"
671     print "test file is incorrectly named it will not be counted, and"
672     print "result in a lower coverage score."
673 metson 1.43 print '----------------------------------------------------------------'
674 metson 1.42 filelist = generate_filelist()
675     tests = 0
676     files = 0
677     pkgcnt = 0
678 metson 1.85 dir = get_relative_path()
679 metson 1.42 pkg = {'name': '', 'files': 0, 'tests': 0}
680     for f in filelist:
681 metson 1.41 testpath = '/'.join([dir, f])
682     pth = testpath.split('./src/python/')
683     pth.append(pth[1].replace('/', '_t/').replace('.', '_t.'))
684 metson 1.42 if pkg['name'] == pth[2].rsplit('/', 1)[0].replace('_t/', '/'):
685     # pkg hasn't changed, increment counts
686     pkg['files'] += 1
687     else:
688     # new package, print stats for old package
689     pkgcnt += 1
690     if pkg['name'] != '' and pkg['files'] > 0:
691     print 'Package %s has coverage %.1f percent' % (pkg['name'],
692     (float(pkg['tests'])/float(pkg['files']) * 100))
693     # and start over for the new package
694     pkg['name'] = pth[2].rsplit('/', 1)[0].replace('_t/', '/')
695     # do global book keeping
696     files += pkg['files']
697     tests += pkg['tests']
698     pkg['files'] = 0
699     pkg['tests'] = 0
700 metson 1.41 pth[1] = 'test/python'
701     testpath = '/'.join(pth)
702 metson 1.42 try:
703     os.stat(testpath)
704     pkg['tests'] += 1
705 metson 1.41 except:
706 metson 1.42 pass
707    
708     coverage = (float(tests) / float(files)) * 100
709 metson 1.43 print '----------------------------------------------------------------'
710 metson 1.42 print 'Code coverage (%s packages) is %.2f percent' % (pkgcnt, coverage)
711 metson 1.41 return coverage
712 metson 1.49
713     class EnvCommand(Command):
714     description = "Configure the PYTHONPATH, DATABASE and PATH variables to" +\
715     "some sensible defaults, if not already set. Call with -q when eval-ing," +\
716 metson 1.50 """ e.g.:
717 metson 1.49 eval `python setup.py -q env`
718     """
719    
720     user_options = [ ]
721    
722     def initialize_options(self):
723     pass
724    
725     def finalize_options(self):
726     pass
727 metson 1.41
728 metson 1.49 def run(self):
729     if not os.getenv('DATABASE', False):
730     # Use an in memory sqlite one if none is configured.
731     print 'export DATABASE=sqlite://'
732    
733 metson 1.93 here = get_relative_path()
734    
735 metson 1.49 tests = here + '/test/python'
736     source = here + '/src/python'
737     webpth = source + '/WMCore/WebTools'
738 metson 1.93
739     pypath=os.getenv('PYTHONPATH', '').strip(':').split(':')
740 metson 1.49
741     for pth in [tests, source]:
742     if pth not in pypath:
743     pypath.append(pth)
744 metson 1.50
745     # We might want to add other executables to PATH
746 metson 1.49 expath=os.getenv('PATH', '').split(':')
747     for pth in [webpth]:
748     if pth not in expath:
749     expath.append(pth)
750    
751     print 'export PYTHONPATH=%s' % ':'.join(pypath)
752     print 'export PATH=%s' % ':'.join(expath)
753 metson 1.85
754     #We want the WMCORE root set, too
755 meloam 1.89 print 'export WMCOREBASE=%s' % get_relative_path()
756 metson 1.85
757 metson 1.49
758 metson 1.22 def getPackages(package_dirs = []):
759     packages = []
760     for dir in package_dirs:
761     for dirpath, dirnames, filenames in os.walk('./%s' % dir):
762     # Exclude things here
763     if dirpath not in ['./src/python/', './src/python/IMProv']:
764     pathelements = dirpath.split('/')
765     if not 'CVS' in pathelements:
766     path = pathelements[3:]
767     packages.append('.'.join(path))
768     return packages
769    
770     package_dir = {'WMCore': 'src/python/WMCore',
771     'WMComponent' : 'src/python/WMComponent',
772     'WMQuality' : 'src/python/WMQuality'}
773    
774     setup (name = 'wmcore',
775     version = '1.0',
776 metson 1.36 maintainer_email = 'hn-cms-wmDevelopment@cern.ch',
777 meloam 1.88 cmdclass = {#'test': TestCommand,
778 metson 1.23 'clean': CleanCommand,
779 metson 1.36 'lint': LintCommand,
780     'report': ReportCommand,
781 metson 1.41 'coverage': CoverageCommand ,
782 metson 1.49 'missing': DumbCoverageCommand,
783 meloam 1.88 'env': EnvCommand,
784     'test' : NoseCommand },
785 metson 1.22 package_dir = package_dir,
786     packages = getPackages(package_dir.values()),)
787 fvlingen 1.7