Package logilab-common-0 :: Package 39 :: Package 0 :: Module pytest
[frames] | no frames]

Source Code for Module logilab-common-0.39.0.pytest

  1  """pytest is a tool that eases test running and debugging. 
  2   
  3  To be able to use pytest, you should either write tests using 
  4  the logilab.common.testlib's framework or the unittest module of the 
  5  Python's standard library. 
  6   
  7  You can customize pytest's behaviour by defining a ``pytestconf.py`` file 
  8  somewhere in your test directory. In this file, you can add options or 
  9  change the way tests are run. 
 10   
 11  To add command line options, you must define a ``update_parser`` function in 
 12  your ``pytestconf.py`` file. The function must accept a single parameter 
 13  that will be the OptionParser's instance to customize. 
 14   
 15  If you wish to customize the tester, you'll have to define a class named 
 16  ``CustomPyTester``. This class should extend the default `PyTester` class 
 17  defined in the pytest module. Take a look at the `PyTester` and `DjangoTester` 
 18  classes for more information about what can be done. 
 19   
 20  For instance, if you wish to add a custom -l option to specify a loglevel, you 
 21  could define the following ``pytestconf.py`` file :: 
 22   
 23      import logging 
 24      from logilab.common.pytest import PyTester 
 25       
 26      def update_parser(parser): 
 27          parser.add_option('-l', '--loglevel', dest='loglevel', action='store', 
 28                            choices=('debug', 'info', 'warning', 'error', 'critical'), 
 29                            default='critical', help="the default log level possible choices are " 
 30                            "('debug', 'info', 'warning', 'error', 'critical')") 
 31          return parser 
 32       
 33       
 34      class CustomPyTester(PyTester): 
 35          def __init__(self, cvg, options): 
 36              super(CustomPyTester, self).__init__(cvg, options) 
 37              loglevel = options.loglevel.upper() 
 38              logger = logging.getLogger('erudi') 
 39              logger.setLevel(logging.getLevelName(loglevel)) 
 40   
 41   
 42  In your TestCase class you can then get the value of a specific option with 
 43  the ``optval`` method:: 
 44       
 45      class MyTestCase(TestCase): 
 46          def test_foo(self): 
 47              loglevel = self.optval('loglevel') 
 48              # ... 
 49   
 50   
 51  You can also tag your tag your test for fine filtering 
 52   
 53  With those tag:: 
 54   
 55      from logilab.common.testlib import tag, TestCase 
 56   
 57      class Exemple(TestCase): 
 58   
 59          @tag('rouge', 'carre') 
 60          def toto(self): 
 61              pass 
 62   
 63          @tag('carre', 'vert') 
 64          def tata(self): 
 65              pass 
 66   
 67          @tag('rouge') 
 68          def titi(test): 
 69              pass 
 70   
 71  you can filter the function with a simpe python expression 
 72   
 73   * ``toto`` and ``titi`` match ``rouge`` 
 74   
 75   * ``toto``, ``tata`` and ``titi``, match ``rouge or carre`` 
 76   
 77   * ``tata`` and ``titi`` match``rouge ^ carre`` 
 78   
 79   * ``titi`` match ``rouge and not carre`` 
 80   
 81   
 82   
 83   
 84               
 85   
 86  :copyright: 2000-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
 87  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr 
 88  :license: General Public License version 2 - http://www.gnu.org/licenses 
 89  """ 
 90  __docformat__ = "restructuredtext en" 
 91   
 92  PYTEST_DOC = """%prog [OPTIONS] [testfile [testpattern]] 
 93   
 94  examples: 
 95   
 96  pytest path/to/mytests.py 
 97  pytest path/to/mytests.py TheseTests 
 98  pytest path/to/mytests.py TheseTests.test_thisone 
 99  pytest path/to/mytests.py -m '(not long and database) or regr' 
100   
101  pytest one (will run both test_thisone and test_thatone) 
102  pytest path/to/mytests.py -s not (will skip test_notthisone) 
103   
104  pytest --coverage test_foo.py 
105    (only if logilab.devtools is available) 
106  """ 
107   
108  import os, sys, re 
109  import os.path as osp 
110  from time import time, clock 
111   
112   
113  from logilab.common.fileutils import abspath_listdir 
114  from logilab.common import testlib 
115  import doctest 
116  import unittest 
117   
118   
119  import imp 
120   
121  import __builtin__ 
122   
123   
124  try: 
125      import django 
126      from logilab.common.modutils import modpath_from_file, load_module_from_modpath 
127      DJANGO_FOUND = True 
128  except ImportError: 
129      DJANGO_FOUND = False 
130   
131  CONF_FILE = 'pytestconf.py' 
132   
133  ## coverage hacks, do not read this, do not read this, do not read this 
134   
135  # hey, but this is an aspect, right ?!!! 
136 -class TraceController(object):
137 nesting = 0 138
139 - def pause_tracing(cls):
140 if not cls.nesting: 141 cls.tracefunc = staticmethod(getattr(sys, '__settrace__', sys.settrace)) 142 cls.oldtracer = getattr(sys, '__tracer__', None) 143 sys.__notrace__ = True 144 cls.tracefunc(None) 145 cls.nesting += 1
146 pause_tracing = classmethod(pause_tracing) 147
148 - def resume_tracing(cls):
149 cls.nesting -= 1 150 assert cls.nesting >= 0 151 if not cls.nesting: 152 cls.tracefunc(cls.oldtracer) 153 delattr(sys, '__notrace__')
154 resume_tracing = classmethod(resume_tracing)
155 156 157 pause_tracing = TraceController.pause_tracing 158 resume_tracing = TraceController.resume_tracing 159 160
161 -def nocoverage(func):
162 if hasattr(func, 'uncovered'): 163 return func 164 func.uncovered = True 165 def not_covered(*args, **kwargs): 166 pause_tracing() 167 try: 168 return func(*args, **kwargs) 169 finally: 170 resume_tracing()
171 not_covered.uncovered = True 172 return not_covered 173 174 175 ## end of coverage hacks 176 177 178 # monkeypatch unittest and doctest (ouch !) 179 unittest.TestCase = testlib.TestCase 180 unittest.main = testlib.unittest_main 181 unittest._TextTestResult = testlib.SkipAwareTestResult 182 unittest.TextTestRunner = testlib.SkipAwareTextTestRunner 183 unittest.TestLoader = testlib.NonStrictTestLoader 184 unittest.TestProgram = testlib.SkipAwareTestProgram 185 if sys.version_info >= (2, 4): 186 doctest.DocTestCase.__bases__ = (testlib.TestCase,) 187 else: 188 unittest.FunctionTestCase.__bases__ = (testlib.TestCase,) 189 190 191 192 TESTFILE_RE = re.compile("^((unit)?test.*|smoketest)\.py$")
193 -def this_is_a_testfile(filename):
194 """returns True if `filename` seems to be a test file""" 195 return TESTFILE_RE.match(osp.basename(filename))
196 197 TESTDIR_RE = re.compile("^(unit)?tests?$")
198 -def this_is_a_testdir(dirpath):
199 """returns True if `filename` seems to be a test directory""" 200 return TESTDIR_RE.match(osp.basename(dirpath))
201 202
203 -def load_pytest_conf(path, parser):
204 """loads a ``pytestconf.py`` file and update default parser 205 and / or tester. 206 """ 207 namespace = {} 208 execfile(path, namespace) 209 if 'update_parser' in namespace: 210 namespace['update_parser'](parser) 211 return namespace.get('CustomPyTester', PyTester)
212 213
214 -def project_root(parser, projdir=os.getcwd()):
215 """try to find project's root and add it to sys.path""" 216 curdir = osp.abspath(projdir) 217 previousdir = curdir 218 testercls = PyTester 219 conf_file_path = osp.join(curdir, CONF_FILE) 220 if osp.isfile(conf_file_path): 221 testercls = load_pytest_conf(conf_file_path, parser) 222 while this_is_a_testdir(curdir) or \ 223 osp.isfile(osp.join(curdir, '__init__.py')): 224 newdir = osp.normpath(osp.join(curdir, os.pardir)) 225 if newdir == curdir: 226 break 227 previousdir = curdir 228 curdir = newdir 229 conf_file_path = osp.join(curdir, CONF_FILE) 230 if osp.isfile(conf_file_path): 231 testercls = load_pytest_conf(conf_file_path, parser) 232 return previousdir, testercls
233 234
235 -class GlobalTestReport(object):
236 """this class holds global test statistics"""
237 - def __init__(self):
238 self.ran = 0 239 self.skipped = 0 240 self.failures = 0 241 self.errors = 0 242 self.ttime = 0 243 self.ctime = 0 244 self.modulescount = 0 245 self.errmodules = []
246
247 - def feed(self, filename, testresult, ttime, ctime):
248 """integrates new test information into internal statistics""" 249 ran = testresult.testsRun 250 self.ran += ran 251 self.skipped += len(getattr(testresult, 'skipped', ())) 252 self.failures += len(testresult.failures) 253 self.errors += len(testresult.errors) 254 self.ttime += ttime 255 self.ctime += ctime 256 self.modulescount += 1 257 if not testresult.wasSuccessful(): 258 problems = len(testresult.failures) + len(testresult.errors) 259 self.errmodules.append((filename[:-3], problems, ran))
260 261
262 - def failed_to_test_module(self, filename):
263 """called when the test module could not be imported by unittest 264 """ 265 self.errors += 1 266 self.modulescount += 1 267 self.ran += 1 268 self.errmodules.append((filename[:-3], 1, 1))
269
270 - def skip_module(self, filename):
271 self.modulescount += 1 272 self.ran += 1 273 self.errmodules.append((filename[:-3], 0, 0))
274
275 - def __str__(self):
276 """this is just presentation stuff""" 277 line1 = ['Ran %s test cases in %.2fs (%.2fs CPU)' 278 % (self.ran, self.ttime, self.ctime)] 279 if self.errors: 280 line1.append('%s errors' % self.errors) 281 if self.failures: 282 line1.append('%s failures' % self.failures) 283 if self.skipped: 284 line1.append('%s skipped' % self.skipped) 285 modulesok = self.modulescount - len(self.errmodules) 286 if self.errors or self.failures: 287 line2 = '%s modules OK (%s failed)' % (modulesok, 288 len(self.errmodules)) 289 descr = ', '.join(['%s [%s/%s]' % info for info in self.errmodules]) 290 line3 = '\nfailures: %s' % descr 291 elif modulesok: 292 line2 = 'All %s modules OK' % modulesok 293 line3 = '' 294 else: 295 return '' 296 return '%s\n%s%s' % (', '.join(line1), line2, line3)
297 298 299
300 -def remove_local_modules_from_sys(testdir):
301 """remove all modules from cache that come from `testdir` 302 303 This is used to avoid strange side-effects when using the 304 testall() mode of pytest. 305 For instance, if we run pytest on this tree:: 306 307 A/test/test_utils.py 308 B/test/test_utils.py 309 310 we **have** to clean sys.modules to make sure the correct test_utils 311 module is ran in B 312 """ 313 for modname, mod in sys.modules.items(): 314 if mod is None: 315 continue 316 if not hasattr(mod, '__file__'): 317 # this is the case of some built-in modules like sys, imp, marshal 318 continue 319 modfile = mod.__file__ 320 # if modfile is not an asbolute path, it was probably loaded locally 321 # during the tests 322 if not osp.isabs(modfile) or modfile.startswith(testdir): 323 del sys.modules[modname]
324 325 326
327 -class PyTester(object):
328 """encaspulates testrun logic""" 329
330 - def __init__(self, cvg, options):
331 self.report = GlobalTestReport() 332 self.cvg = cvg 333 self.options = options 334 self.firstwrite = True
335
336 - def show_report(self):
337 """prints the report and returns appropriate exitcode""" 338 # everything has been ran, print report 339 print "*" * 79 340 print self.report 341 return self.report.failures + self.report.errors
342 343
344 - def testall(self, exitfirst=False):
345 """walks trhough current working directory, finds something 346 which can be considered as a testdir and runs every test there 347 """ 348 here = os.getcwd() 349 for dirname, dirs, _ in os.walk(here): 350 for skipped in ('CVS', '.svn', '.hg'): 351 if skipped in dirs: 352 dirs.remove(skipped) 353 basename = osp.basename(dirname) 354 if this_is_a_testdir(basename): 355 print "going into", dirname 356 # we found a testdir, let's explore it ! 357 self.testonedir(dirname, exitfirst) 358 dirs[:] = [] 359 if self.report.ran == 0: 360 print "no test dir found testing here:", here 361 # if no test was found during the visit, consider 362 # the local directory as a test directory even if 363 # it doesn't have a traditional test directory name 364 self.testonedir(here)
365
366 - def testonedir(self, testdir, exitfirst=False):
367 """finds each testfile in the `testdir` and runs it""" 368 for filename in abspath_listdir(testdir): 369 if this_is_a_testfile(filename): 370 if self.options.exitfirst and not self.options.restart: 371 # overwrite restart file 372 try: 373 restartfile = open(testlib.FILE_RESTART, "w") 374 restartfile.close() 375 except Exception, e: 376 print >> sys.__stderr__, "Error while overwriting \ 377 succeeded test file :", osp.join(os.getcwd(),testlib.FILE_RESTART) 378 raise e 379 # run test and collect information 380 prog = self.testfile(filename, batchmode=True) 381 if exitfirst and (prog is None or not prog.result.wasSuccessful()): 382 break 383 self.firstwrite = True 384 # clean local modules 385 remove_local_modules_from_sys(testdir)
386 387
388 - def testfile(self, filename, batchmode=False):
389 """runs every test in `filename` 390 391 :param filename: an absolute path pointing to a unittest file 392 """ 393 here = os.getcwd() 394 dirname = osp.dirname(filename) 395 if dirname: 396 os.chdir(dirname) 397 # overwrite restart file if it has not been done already 398 if self.options.exitfirst and not self.options.restart and self.firstwrite: 399 try: 400 restartfile = open(testlib.FILE_RESTART, "w") 401 restartfile.close() 402 except Exception, e: 403 print >> sys.__stderr__, "Error while overwriting \ 404 succeeded test file :", osp.join(os.getcwd(),testlib.FILE_RESTART) 405 raise e 406 modname = osp.basename(filename)[:-3] 407 if batchmode: 408 from cStringIO import StringIO 409 outstream = StringIO() 410 else: 411 outstream = sys.stderr 412 try: 413 print >> outstream, (' %s ' % osp.basename(filename)).center(70, '=') 414 except TypeError: # < py 2.4 bw compat 415 print >> outstream, (' %s ' % osp.basename(filename)).center(70) 416 try: 417 tstart, cstart = time(), clock() 418 try: 419 testprog = testlib.unittest_main(modname, batchmode=batchmode, cvg=self.cvg, 420 options=self.options, outstream=outstream) 421 except (KeyboardInterrupt, SystemExit): 422 raise 423 except testlib.TestSkipped: 424 print "Module skipped:", filename 425 self.report.skip_module(filename) 426 return None 427 except Exception: 428 self.report.failed_to_test_module(filename) 429 print >> outstream, 'unhandled exception occured while testing', modname 430 import traceback 431 traceback.print_exc(file=outstream) 432 if batchmode: 433 print >> sys.stderr, outstream.getvalue() 434 return None 435 436 tend, cend = time(), clock() 437 ttime, ctime = (tend - tstart), (cend - cstart) 438 if testprog.result.testsRun and batchmode: 439 print >> sys.stderr, outstream.getvalue() 440 self.report.feed(filename, testprog.result, ttime, ctime) 441 return testprog 442 finally: 443 if dirname: 444 os.chdir(here)
445 446 447
448 -class DjangoTester(PyTester):
449
450 - def load_django_settings(self, dirname):
451 """try to find project's setting and load it""" 452 curdir = osp.abspath(dirname) 453 previousdir = curdir 454 while not osp.isfile(osp.join(curdir, 'settings.py')) and \ 455 osp.isfile(osp.join(curdir, '__init__.py')): 456 newdir = osp.normpath(osp.join(curdir, os.pardir)) 457 if newdir == curdir: 458 raise AssertionError('could not find settings.py') 459 previousdir = curdir 460 curdir = newdir 461 # late django initialization 462 settings = load_module_from_modpath(modpath_from_file(osp.join(curdir, 'settings.py'))) 463 from django.core.management import setup_environ 464 setup_environ(settings) 465 settings.DEBUG = False 466 self.settings = settings 467 # add settings dir to pythonpath since it's the project's root 468 if curdir not in sys.path: 469 sys.path.insert(1, curdir)
470
471 - def before_testfile(self):
472 # Those imports must be done **after** setup_environ was called 473 from django.test.utils import setup_test_environment 474 from django.test.utils import create_test_db 475 setup_test_environment() 476 create_test_db(verbosity=0) 477 self.dbname = self.settings.TEST_DATABASE_NAME
478 479
480 - def after_testfile(self):
481 # Those imports must be done **after** setup_environ was called 482 from django.test.utils import teardown_test_environment 483 from django.test.utils import destroy_test_db 484 teardown_test_environment() 485 print 'destroying', self.dbname 486 destroy_test_db(self.dbname, verbosity=0)
487 488
489 - def testall(self, exitfirst=False):
490 """walks trhough current working directory, finds something 491 which can be considered as a testdir and runs every test there 492 """ 493 for dirname, dirs, _ in os.walk(os.getcwd()): 494 for skipped in ('CVS', '.svn', '.hg'): 495 if skipped in dirs: 496 dirs.remove(skipped) 497 if 'tests.py' in files: 498 self.testonedir(dirname, exitfirst) 499 dirs[:] = [] 500 else: 501 basename = osp.basename(dirname) 502 if basename in ('test', 'tests'): 503 print "going into", dirname 504 # we found a testdir, let's explore it ! 505 self.testonedir(dirname, exitfirst) 506 dirs[:] = []
507 508
509 - def testonedir(self, testdir, exitfirst=False):
510 """finds each testfile in the `testdir` and runs it""" 511 # special django behaviour : if tests are splited in several files, 512 # remove the main tests.py file and tests each test file separately 513 testfiles = [fpath for fpath in abspath_listdir(testdir) 514 if this_is_a_testfile(fpath)] 515 if len(testfiles) > 1: 516 try: 517 testfiles.remove(osp.join(testdir, 'tests.py')) 518 except ValueError: 519 pass 520 for filename in testfiles: 521 # run test and collect information 522 prog = self.testfile(filename, batchmode=True) 523 if exitfirst and (prog is None or not prog.result.wasSuccessful()): 524 break 525 # clean local modules 526 remove_local_modules_from_sys(testdir)
527 528
529 - def testfile(self, filename, batchmode=False):
530 """runs every test in `filename` 531 532 :param filename: an absolute path pointing to a unittest file 533 """ 534 here = os.getcwd() 535 dirname = osp.dirname(filename) 536 if dirname: 537 os.chdir(dirname) 538 self.load_django_settings(dirname) 539 modname = osp.basename(filename)[:-3] 540 print >>sys.stderr, (' %s ' % osp.basename(filename)).center(70, '=') 541 try: 542 try: 543 tstart, cstart = time(), clock() 544 self.before_testfile() 545 testprog = testlib.unittest_main(modname, batchmode=batchmode, cvg=self.cvg) 546 tend, cend = time(), clock() 547 ttime, ctime = (tend - tstart), (cend - cstart) 548 self.report.feed(filename, testprog.result, ttime, ctime) 549 return testprog 550 except SystemExit: 551 raise 552 except Exception, exc: 553 import traceback 554 traceback.print_exc() 555 self.report.failed_to_test_module(filename) 556 print 'unhandled exception occured while testing', modname 557 print 'error: %s' % exc 558 return None 559 finally: 560 self.after_testfile() 561 if dirname: 562 os.chdir(here)
563 564
565 -def make_parser():
566 """creates the OptionParser instance 567 """ 568 from optparse import OptionParser 569 parser = OptionParser(usage=PYTEST_DOC) 570 571 parser.newargs = [] 572 def rebuild_cmdline(option, opt, value, parser): 573 """carry the option to unittest_main""" 574 parser.newargs.append(opt)
575 576 577 def rebuild_and_store(option, opt, value, parser): 578 """carry the option to unittest_main and store 579 the value on current parser 580 """ 581 parser.newargs.append(opt) 582 setattr(parser.values, option.dest, True) 583 584 # pytest options 585 parser.add_option('-t', dest='testdir', default=None, 586 help="directory where the tests will be found") 587 parser.add_option('-d', dest='dbc', default=False, 588 action="store_true", help="enable design-by-contract") 589 # unittest_main options provided and passed through pytest 590 parser.add_option('-v', '--verbose', callback=rebuild_cmdline, 591 action="callback", help="Verbose output") 592 parser.add_option('-i', '--pdb', callback=rebuild_and_store, 593 dest="pdb", action="callback", 594 help="Enable test failure inspection (conflicts with --coverage)") 595 parser.add_option('-x', '--exitfirst', callback=rebuild_and_store, 596 dest="exitfirst", default=False, 597 action="callback", help="Exit on first failure " 598 "(only make sense when pytest run one test file)") 599 parser.add_option('-R', '--restart', callback=rebuild_and_store, 600 dest="restart", default=False, 601 action="callback", 602 help="Restart tests from where it failed (implies exitfirst) " 603 "(only make sense if tests previously ran with exitfirst only)") 604 parser.add_option('-c', '--capture', callback=rebuild_cmdline, 605 action="callback", 606 help="Captures and prints standard out/err only on errors " 607 "(only make sense when pytest run one test file)") 608 parser.add_option('--color', callback=rebuild_cmdline, 609 action="callback", 610 help="colorize tracebacks") 611 parser.add_option('-p', '--printonly', 612 # XXX: I wish I could use the callback action but it 613 # doesn't seem to be able to get the value 614 # associated to the option 615 action="store", dest="printonly", default=None, 616 help="Only prints lines matching specified pattern (implies capture) " 617 "(only make sense when pytest run one test file)") 618 parser.add_option('-s', '--skip', 619 # XXX: I wish I could use the callback action but it 620 # doesn't seem to be able to get the value 621 # associated to the option 622 action="store", dest="skipped", default=None, 623 help="test names matching this name will be skipped " 624 "to skip several patterns, use commas") 625 parser.add_option('-q', '--quiet', callback=rebuild_cmdline, 626 action="callback", help="Minimal output") 627 parser.add_option('-P', '--profile', default=None, dest='profile', 628 help="Profile execution and store data in the given file") 629 parser.add_option('-m', '--match', default=None, dest='tags_pattern', 630 help="only execute test whose tag macht the current pattern") 631 632 try: 633 from logilab.devtools.lib.coverage import Coverage 634 parser.add_option('--coverage', dest="coverage", default=False, 635 action="store_true", 636 help="run tests with pycoverage (conflicts with --pdb)") 637 except ImportError: 638 pass 639 640 if DJANGO_FOUND: 641 parser.add_option('-J', '--django', dest='django', default=False, 642 action="store_true", 643 help='use pytest for django test cases') 644 return parser 645 646
647 -def parseargs(parser):
648 """Parse the command line and return (options processed), (options to pass to 649 unittest_main()), (explicitfile or None). 650 """ 651 # parse the command line 652 options, args = parser.parse_args() 653 if options.pdb and getattr(options, 'coverage', False): 654 parser.error("'pdb' and 'coverage' options are exclusive") 655 filenames = [arg for arg in args if arg.endswith('.py')] 656 if filenames: 657 if len(filenames) > 1: 658 parser.error("only one filename is acceptable") 659 explicitfile = filenames[0] 660 args.remove(explicitfile) 661 else: 662 explicitfile = None 663 # someone wants DBC 664 testlib.ENABLE_DBC = options.dbc 665 newargs = parser.newargs 666 if options.printonly: 667 newargs.extend(['--printonly', options.printonly]) 668 if options.skipped: 669 newargs.extend(['--skip', options.skipped]) 670 # restart implies exitfirst 671 if options.restart: 672 options.exitfirst = True 673 # append additional args to the new sys.argv and let unittest_main 674 # do the rest 675 newargs += args 676 return options, explicitfile
677 678 679
680 -def run():
681 parser = make_parser() 682 rootdir, testercls = project_root(parser) 683 options, explicitfile = parseargs(parser) 684 # mock a new command line 685 sys.argv[1:] = parser.newargs 686 covermode = getattr(options, 'coverage', None) 687 cvg = None 688 if not '' in sys.path: 689 sys.path.insert(0, '') 690 if covermode: 691 # control_import_coverage(rootdir) 692 from logilab.devtools.lib.coverage import Coverage 693 cvg = Coverage([rootdir]) 694 cvg.erase() 695 cvg.start() 696 if DJANGO_FOUND and options.django: 697 tester = DjangoTester(cvg, options) 698 else: 699 tester = testercls(cvg, options) 700 if explicitfile: 701 cmd, args = tester.testfile, (explicitfile,) 702 elif options.testdir: 703 cmd, args = tester.testonedir, (options.testdir, options.exitfirst) 704 else: 705 cmd, args = tester.testall, (options.exitfirst,) 706 try: 707 try: 708 if options.profile: 709 import hotshot 710 prof = hotshot.Profile(options.profile) 711 prof.runcall(cmd, *args) 712 prof.close() 713 print 'profile data saved in', options.profile 714 else: 715 cmd(*args) 716 except SystemExit: 717 raise 718 except: 719 import traceback 720 traceback.print_exc() 721 finally: 722 errcode = tester.show_report() 723 if covermode: 724 cvg.stop() 725 cvg.save() 726 here = osp.abspath(os.getcwd()) 727 if this_is_a_testdir(here): 728 morfdir = osp.normpath(osp.join(here, '..')) 729 else: 730 morfdir = here 731 print "computing code coverage (%s), this might take some time" % \ 732 morfdir 733 cvg.annotate([morfdir]) 734 cvg.report([morfdir], False) 735 sys.exit(errcode)
736