ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/WMCORE/setup.py
Revision: 1.86
Committed: Thu Jan 28 18:07:30 2010 UTC (15 years, 3 months ago) by metson
Content type: text/x-python
Branch: MAIN
Changes since 1.85: +15 -6 lines
Log Message:
Turn on detailed lint reports by adding a -r flag

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