~ubuntu-branches/ubuntu/quantal/virtinst/quantal-proposed

« back to all changes in this revision

Viewing changes to tests/coverage.py

  • Committer: Bazaar Package Importer
  • Author(s): Guido Günther
  • Date: 2009-12-07 10:04:22 UTC
  • mto: (1.6.3 sid)
  • mto: This revision was merged to the branch mainline in revision 20.
  • Revision ID: james.westby@ubuntu.com-20091207100422-2izjffd5ljvqun47
Tags: upstream-0.500.1
Import upstream version 0.500.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
#
3
 
#             Perforce Defect Tracking Integration Project
4
 
#              <http://www.ravenbrook.com/project/p4dti/>
5
 
#
6
 
#                   COVERAGE.PY -- COVERAGE TESTING
7
 
#
8
 
#             Gareth Rees, Ravenbrook Limited, 2001-12-04
9
 
#                     Ned Batchelder, 2004-12-12
10
 
#         http://nedbatchelder.com/code/modules/coverage.html
11
 
#
12
 
#
13
 
# 1. INTRODUCTION
14
 
#
15
 
# This module provides coverage testing for Python code.
16
 
#
17
 
# The intended readership is all Python developers.
18
 
#
19
 
# This document is not confidential.
20
 
#
21
 
# See [GDR 2001-12-04a] for the command-line interface, programmatic
22
 
# interface and limitations.  See [GDR 2001-12-04b] for requirements and
23
 
# design.
24
 
 
25
 
r"""Usage:
26
 
 
27
 
coverage.py -x [-p] MODULE.py [ARG1 ARG2 ...]
28
 
    Execute module, passing the given command-line arguments, collecting
29
 
    coverage data. With the -p option, write to a temporary file containing
30
 
    the machine name and process ID.
31
 
 
32
 
coverage.py -e
33
 
    Erase collected coverage data.
34
 
 
35
 
coverage.py -c
36
 
    Collect data from multiple coverage files (as created by -p option above)
37
 
    and store it into a single file representing the union of the coverage.
38
 
 
39
 
coverage.py -r [-m] [-o dir1,dir2,...] FILE1 FILE2 ...
40
 
    Report on the statement coverage for the given files.  With the -m
41
 
    option, show line numbers of the statements that weren't executed.
42
 
 
43
 
coverage.py -a [-d dir] [-o dir1,dir2,...] FILE1 FILE2 ...
44
 
    Make annotated copies of the given files, marking statements that
45
 
    are executed with > and statements that are missed with !.  With
46
 
    the -d option, make the copies in that directory.  Without the -d
47
 
    option, make each copy in the same directory as the original.
48
 
 
49
 
-o dir,dir2,...
50
 
  Omit reporting or annotating files when their filename path starts with
51
 
  a directory listed in the omit list.
52
 
  e.g. python coverage.py -i -r -o c:\python23,lib\enthought\traits
53
 
 
54
 
Coverage data is saved in the file .coverage by default.  Set the
55
 
COVERAGE_FILE environment variable to save it somewhere else."""
56
 
 
57
 
__version__ = "2.85.20080914"    # see detailed history at the end of this file.
58
 
 
59
 
import compiler
60
 
import compiler.visitor
61
 
import glob
62
 
import os
63
 
import re
64
 
import string
65
 
import symbol
66
 
import sys
67
 
import threading
68
 
import token
69
 
import types
70
 
import zipimport
71
 
from socket import gethostname
72
 
 
73
 
# Python version compatibility
74
 
try:
75
 
    strclass = basestring   # new to 2.3
76
 
except:
77
 
    strclass = str
78
 
 
79
 
# 2. IMPLEMENTATION
80
 
#
81
 
# This uses the "singleton" pattern.
82
 
#
83
 
# The word "morf" means a module object (from which the source file can
84
 
# be deduced by suitable manipulation of the __file__ attribute) or a
85
 
# filename.
86
 
#
87
 
# When we generate a coverage report we have to canonicalize every
88
 
# filename in the coverage dictionary just in case it refers to the
89
 
# module we are reporting on.  It seems a shame to throw away this
90
 
# information so the data in the coverage dictionary is transferred to
91
 
# the 'cexecuted' dictionary under the canonical filenames.
92
 
#
93
 
# The coverage dictionary is called "c" and the trace function "t".  The
94
 
# reason for these short names is that Python looks up variables by name
95
 
# at runtime and so execution time depends on the length of variables!
96
 
# In the bottleneck of this application it's appropriate to abbreviate
97
 
# names to increase speed.
98
 
 
99
 
class StatementFindingAstVisitor(compiler.visitor.ASTVisitor):
100
 
    """ A visitor for a parsed Abstract Syntax Tree which finds executable
101
 
        statements.
102
 
    """
103
 
    def __init__(self, statements, excluded, suite_spots):
104
 
        compiler.visitor.ASTVisitor.__init__(self)
105
 
        self.statements = statements
106
 
        self.excluded = excluded
107
 
        self.suite_spots = suite_spots
108
 
        self.excluding_suite = 0
109
 
        
110
 
    def doRecursive(self, node):
111
 
        for n in node.getChildNodes():
112
 
            self.dispatch(n)
113
 
 
114
 
    visitStmt = visitModule = doRecursive
115
 
    
116
 
    def doCode(self, node):
117
 
        if hasattr(node, 'decorators') and node.decorators:
118
 
            self.dispatch(node.decorators)
119
 
            self.recordAndDispatch(node.code)
120
 
        else:
121
 
            self.doSuite(node, node.code)
122
 
            
123
 
    visitFunction = visitClass = doCode
124
 
 
125
 
    def getFirstLine(self, node):
126
 
        # Find the first line in the tree node.
127
 
        lineno = node.lineno
128
 
        for n in node.getChildNodes():
129
 
            f = self.getFirstLine(n)
130
 
            if lineno and f:
131
 
                lineno = min(lineno, f)
132
 
            else:
133
 
                lineno = lineno or f
134
 
        return lineno
135
 
 
136
 
    def getLastLine(self, node):
137
 
        # Find the first line in the tree node.
138
 
        lineno = node.lineno
139
 
        for n in node.getChildNodes():
140
 
            lineno = max(lineno, self.getLastLine(n))
141
 
        return lineno
142
 
    
143
 
    def doStatement(self, node):
144
 
        self.recordLine(self.getFirstLine(node))
145
 
 
146
 
    visitAssert = visitAssign = visitAssTuple = visitPrint = \
147
 
        visitPrintnl = visitRaise = visitSubscript = visitDecorators = \
148
 
        doStatement
149
 
    
150
 
    def visitPass(self, node):
151
 
        # Pass statements have weird interactions with docstrings.  If this
152
 
        # pass statement is part of one of those pairs, claim that the statement
153
 
        # is on the later of the two lines.
154
 
        l = node.lineno
155
 
        if l:
156
 
            lines = self.suite_spots.get(l, [l,l])
157
 
            self.statements[lines[1]] = 1
158
 
        
159
 
    def visitDiscard(self, node):
160
 
        # Discard nodes are statements that execute an expression, but then
161
 
        # discard the results.  This includes function calls, so we can't 
162
 
        # ignore them all.  But if the expression is a constant, the statement
163
 
        # won't be "executed", so don't count it now.
164
 
        if node.expr.__class__.__name__ != 'Const':
165
 
            self.doStatement(node)
166
 
 
167
 
    def recordNodeLine(self, node):
168
 
        # Stmt nodes often have None, but shouldn't claim the first line of
169
 
        # their children (because the first child might be an ignorable line
170
 
        # like "global a").
171
 
        if node.__class__.__name__ != 'Stmt':
172
 
            return self.recordLine(self.getFirstLine(node))
173
 
        else:
174
 
            return 0
175
 
    
176
 
    def recordLine(self, lineno):
177
 
        # Returns a bool, whether the line is included or excluded.
178
 
        if lineno:
179
 
            # Multi-line tests introducing suites have to get charged to their
180
 
            # keyword.
181
 
            if lineno in self.suite_spots:
182
 
                lineno = self.suite_spots[lineno][0]
183
 
            # If we're inside an excluded suite, record that this line was
184
 
            # excluded.
185
 
            if self.excluding_suite:
186
 
                self.excluded[lineno] = 1
187
 
                return 0
188
 
            # If this line is excluded, or suite_spots maps this line to
189
 
            # another line that is exlcuded, then we're excluded.
190
 
            elif self.excluded.has_key(lineno) or \
191
 
                 self.suite_spots.has_key(lineno) and \
192
 
                 self.excluded.has_key(self.suite_spots[lineno][1]):
193
 
                return 0
194
 
            # Otherwise, this is an executable line.
195
 
            else:
196
 
                self.statements[lineno] = 1
197
 
                return 1
198
 
        return 0
199
 
    
200
 
    default = recordNodeLine
201
 
    
202
 
    def recordAndDispatch(self, node):
203
 
        self.recordNodeLine(node)
204
 
        self.dispatch(node)
205
 
 
206
 
    def doSuite(self, intro, body, exclude=0):
207
 
        exsuite = self.excluding_suite
208
 
        if exclude or (intro and not self.recordNodeLine(intro)):
209
 
            self.excluding_suite = 1
210
 
        self.recordAndDispatch(body)
211
 
        self.excluding_suite = exsuite
212
 
        
213
 
    def doPlainWordSuite(self, prevsuite, suite):
214
 
        # Finding the exclude lines for else's is tricky, because they aren't
215
 
        # present in the compiler parse tree.  Look at the previous suite,
216
 
        # and find its last line.  If any line between there and the else's
217
 
        # first line are excluded, then we exclude the else.
218
 
        lastprev = self.getLastLine(prevsuite)
219
 
        firstelse = self.getFirstLine(suite)
220
 
        for l in range(lastprev+1, firstelse):
221
 
            if self.suite_spots.has_key(l):
222
 
                self.doSuite(None, suite, exclude=self.excluded.has_key(l))
223
 
                break
224
 
        else:
225
 
            self.doSuite(None, suite)
226
 
        
227
 
    def doElse(self, prevsuite, node):
228
 
        if node.else_:
229
 
            self.doPlainWordSuite(prevsuite, node.else_)
230
 
    
231
 
    def visitFor(self, node):
232
 
        self.doSuite(node, node.body)
233
 
        self.doElse(node.body, node)
234
 
 
235
 
    visitWhile = visitFor
236
 
 
237
 
    def visitIf(self, node):
238
 
        # The first test has to be handled separately from the rest.
239
 
        # The first test is credited to the line with the "if", but the others
240
 
        # are credited to the line with the test for the elif.
241
 
        self.doSuite(node, node.tests[0][1])
242
 
        for t, n in node.tests[1:]:
243
 
            self.doSuite(t, n)
244
 
        self.doElse(node.tests[-1][1], node)
245
 
 
246
 
    def visitTryExcept(self, node):
247
 
        self.doSuite(node, node.body)
248
 
        for i in range(len(node.handlers)):
249
 
            a, b, h = node.handlers[i]
250
 
            if not a:
251
 
                # It's a plain "except:".  Find the previous suite.
252
 
                if i > 0:
253
 
                    prev = node.handlers[i-1][2]
254
 
                else:
255
 
                    prev = node.body
256
 
                self.doPlainWordSuite(prev, h)
257
 
            else:
258
 
                self.doSuite(a, h)
259
 
        self.doElse(node.handlers[-1][2], node)
260
 
    
261
 
    def visitTryFinally(self, node):
262
 
        self.doSuite(node, node.body)
263
 
        self.doPlainWordSuite(node.body, node.final)
264
 
        
265
 
    def visitWith(self, node):
266
 
        self.doSuite(node, node.body)
267
 
        
268
 
    def visitGlobal(self, node):
269
 
        # "global" statements don't execute like others (they don't call the
270
 
        # trace function), so don't record their line numbers.
271
 
        pass
272
 
 
273
 
the_coverage = None
274
 
 
275
 
class CoverageException(Exception):
276
 
    pass
277
 
 
278
 
class coverage:
279
 
    # Name of the cache file (unless environment variable is set).
280
 
    cache_default = ".coverage"
281
 
 
282
 
    # Environment variable naming the cache file.
283
 
    cache_env = "COVERAGE_FILE"
284
 
 
285
 
    # A dictionary with an entry for (Python source file name, line number
286
 
    # in that file) if that line has been executed.
287
 
    c = {}
288
 
    
289
 
    # A map from canonical Python source file name to a dictionary in
290
 
    # which there's an entry for each line number that has been
291
 
    # executed.
292
 
    cexecuted = {}
293
 
 
294
 
    # Cache of results of calling the analysis2() method, so that you can
295
 
    # specify both -r and -a without doing double work.
296
 
    analysis_cache = {}
297
 
 
298
 
    # Cache of results of calling the canonical_filename() method, to
299
 
    # avoid duplicating work.
300
 
    canonical_filename_cache = {}
301
 
 
302
 
    def __init__(self):
303
 
        global the_coverage
304
 
        if the_coverage:
305
 
            raise CoverageException("Only one coverage object allowed.")
306
 
        self.usecache = 1
307
 
        self.cache = None
308
 
        self.parallel_mode = False
309
 
        self.exclude_re = ''
310
 
        self.nesting = 0
311
 
        self.cstack = []
312
 
        self.xstack = []
313
 
        self.relative_dir = self.abs_file(os.curdir)+os.sep
314
 
        self.exclude('# *pragma[: ]*[nN][oO] *[cC][oO][vV][eE][rR]')
315
 
 
316
 
    # t(f, x, y).  This method is passed to sys.settrace as a trace function.  
317
 
    # See [van Rossum 2001-07-20b, 9.2] for an explanation of sys.settrace and 
318
 
    # the arguments and return value of the trace function.
319
 
    # See [van Rossum 2001-07-20a, 3.2] for a description of frame and code
320
 
    # objects.
321
 
    
322
 
    def t(self, f, w, unused):                                 #pragma: no cover
323
 
        if w == 'line':
324
 
            self.c[(f.f_code.co_filename, f.f_lineno)] = 1
325
 
            #-for c in self.cstack:
326
 
            #-    c[(f.f_code.co_filename, f.f_lineno)] = 1
327
 
        return self.t
328
 
    
329
 
    def help(self, error=None):     #pragma: no cover
330
 
        if error:
331
 
            print error
332
 
            print
333
 
        print __doc__
334
 
        sys.exit(1)
335
 
 
336
 
    def command_line(self, argv, help_fn=None):
337
 
        import getopt
338
 
        help_fn = help_fn or self.help
339
 
        settings = {}
340
 
        optmap = {
341
 
            '-a': 'annotate',
342
 
            '-c': 'collect',
343
 
            '-d:': 'directory=',
344
 
            '-e': 'erase',
345
 
            '-h': 'help',
346
 
            '-i': 'ignore-errors',
347
 
            '-m': 'show-missing',
348
 
            '-p': 'parallel-mode',
349
 
            '-r': 'report',
350
 
            '-x': 'execute',
351
 
            '-o:': 'omit=',
352
 
            }
353
 
        short_opts = string.join(map(lambda o: o[1:], optmap.keys()), '')
354
 
        long_opts = optmap.values()
355
 
        options, args = getopt.getopt(argv, short_opts, long_opts)
356
 
        for o, a in options:
357
 
            if optmap.has_key(o):
358
 
                settings[optmap[o]] = 1
359
 
            elif optmap.has_key(o + ':'):
360
 
                settings[optmap[o + ':']] = a
361
 
            elif o[2:] in long_opts:
362
 
                settings[o[2:]] = 1
363
 
            elif o[2:] + '=' in long_opts:
364
 
                settings[o[2:]+'='] = a
365
 
            else:       #pragma: no cover
366
 
                pass    # Can't get here, because getopt won't return anything unknown.
367
 
 
368
 
        if settings.get('help'):
369
 
            help_fn()
370
 
 
371
 
        for i in ['erase', 'execute']:
372
 
            for j in ['annotate', 'report', 'collect']:
373
 
                if settings.get(i) and settings.get(j):
374
 
                    help_fn("You can't specify the '%s' and '%s' "
375
 
                              "options at the same time." % (i, j))
376
 
 
377
 
        args_needed = (settings.get('execute')
378
 
                       or settings.get('annotate')
379
 
                       or settings.get('report'))
380
 
        action = (settings.get('erase') 
381
 
                  or settings.get('collect')
382
 
                  or args_needed)
383
 
        if not action:
384
 
            help_fn("You must specify at least one of -e, -x, -c, -r, or -a.")
385
 
        if not args_needed and args:
386
 
            help_fn("Unexpected arguments: %s" % " ".join(args))
387
 
        
388
 
        self.parallel_mode = settings.get('parallel-mode')
389
 
        self.get_ready()
390
 
 
391
 
        if settings.get('erase'):
392
 
            self.erase()
393
 
        if settings.get('execute'):
394
 
            if not args:
395
 
                help_fn("Nothing to do.")
396
 
            sys.argv = args
397
 
            self.start()
398
 
            import __main__
399
 
            sys.path[0] = os.path.dirname(sys.argv[0])
400
 
            execfile(sys.argv[0], __main__.__dict__)
401
 
        if settings.get('collect'):
402
 
            self.collect()
403
 
        if not args:
404
 
            args = self.cexecuted.keys()
405
 
        
406
 
        ignore_errors = settings.get('ignore-errors')
407
 
        show_missing = settings.get('show-missing')
408
 
        directory = settings.get('directory=')
409
 
 
410
 
        omit = settings.get('omit=')
411
 
        if omit is not None:
412
 
            omit = [self.abs_file(p) for p in omit.split(',')]
413
 
        else:
414
 
            omit = []
415
 
        
416
 
        if settings.get('report'):
417
 
            self.report(args, show_missing, ignore_errors, omit_prefixes=omit)
418
 
        if settings.get('annotate'):
419
 
            self.annotate(args, directory, ignore_errors, omit_prefixes=omit)
420
 
 
421
 
    def use_cache(self, usecache, cache_file=None):
422
 
        self.usecache = usecache
423
 
        if cache_file and not self.cache:
424
 
            self.cache_default = cache_file
425
 
        
426
 
    def get_ready(self, parallel_mode=False):
427
 
        if self.usecache and not self.cache:
428
 
            self.cache = os.environ.get(self.cache_env, self.cache_default)
429
 
            if self.parallel_mode:
430
 
                self.cache += "." + gethostname() + "." + str(os.getpid())
431
 
            self.restore()
432
 
        self.analysis_cache = {}
433
 
        
434
 
    def start(self, parallel_mode=False):
435
 
        self.get_ready()
436
 
        if self.nesting == 0:                               #pragma: no cover
437
 
            sys.settrace(self.t)
438
 
            if hasattr(threading, 'settrace'):
439
 
                threading.settrace(self.t)
440
 
        self.nesting += 1
441
 
        
442
 
    def stop(self):
443
 
        self.nesting -= 1
444
 
        if self.nesting == 0:                               #pragma: no cover
445
 
            sys.settrace(None)
446
 
            if hasattr(threading, 'settrace'):
447
 
                threading.settrace(None)
448
 
 
449
 
    def erase(self):
450
 
        self.get_ready()
451
 
        self.c = {}
452
 
        self.analysis_cache = {}
453
 
        self.cexecuted = {}
454
 
        if self.cache and os.path.exists(self.cache):
455
 
            os.remove(self.cache)
456
 
 
457
 
    def exclude(self, re):
458
 
        if self.exclude_re:
459
 
            self.exclude_re += "|"
460
 
        self.exclude_re += "(" + re + ")"
461
 
 
462
 
    def begin_recursive(self):
463
 
        self.cstack.append(self.c)
464
 
        self.xstack.append(self.exclude_re)
465
 
        
466
 
    def end_recursive(self):
467
 
        self.c = self.cstack.pop()
468
 
        self.exclude_re = self.xstack.pop()
469
 
 
470
 
    # save().  Save coverage data to the coverage cache.
471
 
 
472
 
    def save(self):
473
 
        if self.usecache and self.cache:
474
 
            self.canonicalize_filenames()
475
 
            cache = open(self.cache, 'wb')
476
 
            import marshal
477
 
            marshal.dump(self.cexecuted, cache)
478
 
            cache.close()
479
 
 
480
 
    # restore().  Restore coverage data from the coverage cache (if it exists).
481
 
 
482
 
    def restore(self):
483
 
        self.c = {}
484
 
        self.cexecuted = {}
485
 
        assert self.usecache
486
 
        if os.path.exists(self.cache):
487
 
            self.cexecuted = self.restore_file(self.cache)
488
 
 
489
 
    def restore_file(self, file_name):
490
 
        try:
491
 
            cache = open(file_name, 'rb')
492
 
            import marshal
493
 
            cexecuted = marshal.load(cache)
494
 
            cache.close()
495
 
            if isinstance(cexecuted, types.DictType):
496
 
                return cexecuted
497
 
            else:
498
 
                return {}
499
 
        except:
500
 
            return {}
501
 
 
502
 
    # collect(). Collect data in multiple files produced by parallel mode
503
 
 
504
 
    def collect(self):
505
 
        cache_dir, local = os.path.split(self.cache)
506
 
        for f in os.listdir(cache_dir or '.'):
507
 
            if not f.startswith(local):
508
 
                continue
509
 
 
510
 
            full_path = os.path.join(cache_dir, f)
511
 
            cexecuted = self.restore_file(full_path)
512
 
            self.merge_data(cexecuted)
513
 
 
514
 
    def merge_data(self, new_data):
515
 
        for file_name, file_data in new_data.items():
516
 
            if self.cexecuted.has_key(file_name):
517
 
                self.merge_file_data(self.cexecuted[file_name], file_data)
518
 
            else:
519
 
                self.cexecuted[file_name] = file_data
520
 
 
521
 
    def merge_file_data(self, cache_data, new_data):
522
 
        for line_number in new_data.keys():
523
 
            if not cache_data.has_key(line_number):
524
 
                cache_data[line_number] = new_data[line_number]
525
 
 
526
 
    def abs_file(self, filename):
527
 
        """ Helper function to turn a filename into an absolute normalized
528
 
            filename.
529
 
        """
530
 
        return os.path.normcase(os.path.abspath(os.path.realpath(filename)))
531
 
 
532
 
    def get_zip_data(self, filename):
533
 
        """ Get data from `filename` if it is a zip file path, or return None
534
 
            if it is not.
535
 
        """
536
 
        markers = ['.zip'+os.sep, '.egg'+os.sep]
537
 
        for marker in markers:
538
 
            if marker in filename:
539
 
                parts = filename.split(marker)
540
 
                try:
541
 
                    zi = zipimport.zipimporter(parts[0]+marker[:-1])
542
 
                except zipimport.ZipImportError:
543
 
                    continue
544
 
                try:
545
 
                    data = zi.get_data(parts[1])
546
 
                except IOError:
547
 
                    continue
548
 
                return data
549
 
        return None
550
 
 
551
 
    # canonical_filename(filename).  Return a canonical filename for the
552
 
    # file (that is, an absolute path with no redundant components and
553
 
    # normalized case).  See [GDR 2001-12-04b, 3.3].
554
 
 
555
 
    def canonical_filename(self, filename):
556
 
        if not self.canonical_filename_cache.has_key(filename):
557
 
            f = filename
558
 
            if os.path.isabs(f) and not os.path.exists(f):
559
 
                if not self.get_zip_data(f):
560
 
                    f = os.path.basename(f)
561
 
            if not os.path.isabs(f):
562
 
                for path in [os.curdir] + sys.path:
563
 
                    g = os.path.join(path, f)
564
 
                    if os.path.exists(g):
565
 
                        f = g
566
 
                        break
567
 
            cf = self.abs_file(f)
568
 
            self.canonical_filename_cache[filename] = cf
569
 
        return self.canonical_filename_cache[filename]
570
 
 
571
 
    # canonicalize_filenames().  Copy results from "c" to "cexecuted", 
572
 
    # canonicalizing filenames on the way.  Clear the "c" map.
573
 
 
574
 
    def canonicalize_filenames(self):
575
 
        for filename, lineno in self.c.keys():
576
 
            if filename == '<string>':
577
 
                # Can't do anything useful with exec'd strings, so skip them.
578
 
                continue
579
 
            f = self.canonical_filename(filename)
580
 
            if not self.cexecuted.has_key(f):
581
 
                self.cexecuted[f] = {}
582
 
            self.cexecuted[f][lineno] = 1
583
 
        self.c = {}
584
 
 
585
 
    # morf_filename(morf).  Return the filename for a module or file.
586
 
 
587
 
    def morf_filename(self, morf):
588
 
        if hasattr(morf, '__file__'):
589
 
            f = morf.__file__
590
 
        else:
591
 
            f = morf
592
 
        return self.canonical_filename(f)
593
 
 
594
 
    # analyze_morf(morf).  Analyze the module or filename passed as
595
 
    # the argument.  If the source code can't be found, raise an error.
596
 
    # Otherwise, return a tuple of (1) the canonical filename of the
597
 
    # source code for the module, (2) a list of lines of statements
598
 
    # in the source code, (3) a list of lines of excluded statements,
599
 
    # and (4), a map of line numbers to multi-line line number ranges, for
600
 
    # statements that cross lines.
601
 
    
602
 
    def analyze_morf(self, morf):
603
 
        if self.analysis_cache.has_key(morf):
604
 
            return self.analysis_cache[morf]
605
 
        filename = self.morf_filename(morf)
606
 
        ext = os.path.splitext(filename)[1]
607
 
        source, sourcef = None, None
608
 
        if ext == '.pyc':
609
 
            if not os.path.exists(filename[:-1]):
610
 
                source = self.get_zip_data(filename[:-1])
611
 
                if not source:
612
 
                    raise CoverageException(
613
 
                        "No source for compiled code '%s'." % filename
614
 
                        )
615
 
            filename = filename[:-1]
616
 
        if not source:
617
 
            sourcef = open(filename, 'rU')
618
 
            source = sourcef.read()
619
 
        try:
620
 
            lines, excluded_lines, line_map = self.find_executable_statements(
621
 
                source, exclude=self.exclude_re
622
 
                )
623
 
        except SyntaxError, synerr:
624
 
            raise CoverageException(
625
 
                "Couldn't parse '%s' as Python source: '%s' at line %d" %
626
 
                    (filename, synerr.msg, synerr.lineno)
627
 
                )
628
 
        if sourcef:
629
 
            sourcef.close()
630
 
        result = filename, lines, excluded_lines, line_map
631
 
        self.analysis_cache[morf] = result
632
 
        return result
633
 
 
634
 
    def first_line_of_tree(self, tree):
635
 
        while True:
636
 
            if len(tree) == 3 and type(tree[2]) == type(1):
637
 
                return tree[2]
638
 
            tree = tree[1]
639
 
    
640
 
    def last_line_of_tree(self, tree):
641
 
        while True:
642
 
            if len(tree) == 3 and type(tree[2]) == type(1):
643
 
                return tree[2]
644
 
            tree = tree[-1]
645
 
    
646
 
    def find_docstring_pass_pair(self, tree, spots):
647
 
        for i in range(1, len(tree)):
648
 
            if self.is_string_constant(tree[i]) and self.is_pass_stmt(tree[i+1]):
649
 
                first_line = self.first_line_of_tree(tree[i])
650
 
                last_line = self.last_line_of_tree(tree[i+1])
651
 
                self.record_multiline(spots, first_line, last_line)
652
 
        
653
 
    def is_string_constant(self, tree):
654
 
        try:
655
 
            return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.expr_stmt
656
 
        except:
657
 
            return False
658
 
        
659
 
    def is_pass_stmt(self, tree):
660
 
        try:
661
 
            return tree[0] == symbol.stmt and tree[1][1][1][0] == symbol.pass_stmt
662
 
        except:
663
 
            return False
664
 
 
665
 
    def record_multiline(self, spots, i, j):
666
 
        for l in range(i, j+1):
667
 
            spots[l] = (i, j)
668
 
            
669
 
    def get_suite_spots(self, tree, spots):
670
 
        """ Analyze a parse tree to find suite introducers which span a number
671
 
            of lines.
672
 
        """
673
 
        for i in range(1, len(tree)):
674
 
            if type(tree[i]) == type(()):
675
 
                if tree[i][0] == symbol.suite:
676
 
                    # Found a suite, look back for the colon and keyword.
677
 
                    lineno_colon = lineno_word = None
678
 
                    for j in range(i-1, 0, -1):
679
 
                        if tree[j][0] == token.COLON:
680
 
                            # Colons are never executed themselves: we want the
681
 
                            # line number of the last token before the colon.
682
 
                            lineno_colon = self.last_line_of_tree(tree[j-1])
683
 
                        elif tree[j][0] == token.NAME:
684
 
                            if tree[j][1] == 'elif':
685
 
                                # Find the line number of the first non-terminal
686
 
                                # after the keyword.
687
 
                                t = tree[j+1]
688
 
                                while t and token.ISNONTERMINAL(t[0]):
689
 
                                    t = t[1]
690
 
                                if t:
691
 
                                    lineno_word = t[2]
692
 
                            else:
693
 
                                lineno_word = tree[j][2]
694
 
                            break
695
 
                        elif tree[j][0] == symbol.except_clause:
696
 
                            # "except" clauses look like:
697
 
                            # ('except_clause', ('NAME', 'except', lineno), ...)
698
 
                            if tree[j][1][0] == token.NAME:
699
 
                                lineno_word = tree[j][1][2]
700
 
                                break
701
 
                    if lineno_colon and lineno_word:
702
 
                        # Found colon and keyword, mark all the lines
703
 
                        # between the two with the two line numbers.
704
 
                        self.record_multiline(spots, lineno_word, lineno_colon)
705
 
 
706
 
                    # "pass" statements are tricky: different versions of Python
707
 
                    # treat them differently, especially in the common case of a
708
 
                    # function with a doc string and a single pass statement.
709
 
                    self.find_docstring_pass_pair(tree[i], spots)
710
 
                    
711
 
                elif tree[i][0] == symbol.simple_stmt:
712
 
                    first_line = self.first_line_of_tree(tree[i])
713
 
                    last_line = self.last_line_of_tree(tree[i])
714
 
                    if first_line != last_line:
715
 
                        self.record_multiline(spots, first_line, last_line)
716
 
                self.get_suite_spots(tree[i], spots)
717
 
 
718
 
    def find_executable_statements(self, text, exclude=None):
719
 
        # Find lines which match an exclusion pattern.
720
 
        excluded = {}
721
 
        suite_spots = {}
722
 
        if exclude:
723
 
            reExclude = re.compile(exclude)
724
 
            lines = text.split('\n')
725
 
            for i in range(len(lines)):
726
 
                if reExclude.search(lines[i]):
727
 
                    excluded[i+1] = 1
728
 
 
729
 
        # Parse the code and analyze the parse tree to find out which statements
730
 
        # are multiline, and where suites begin and end.
731
 
        import parser
732
 
        tree = parser.suite(text+'\n\n').totuple(1)
733
 
        self.get_suite_spots(tree, suite_spots)
734
 
        #print "Suite spots:", suite_spots
735
 
        
736
 
        # Use the compiler module to parse the text and find the executable
737
 
        # statements.  We add newlines to be impervious to final partial lines.
738
 
        statements = {}
739
 
        ast = compiler.parse(text+'\n\n')
740
 
        visitor = StatementFindingAstVisitor(statements, excluded, suite_spots)
741
 
        compiler.walk(ast, visitor, walker=visitor)
742
 
 
743
 
        lines = statements.keys()
744
 
        lines.sort()
745
 
        excluded_lines = excluded.keys()
746
 
        excluded_lines.sort()
747
 
        return lines, excluded_lines, suite_spots
748
 
 
749
 
    # format_lines(statements, lines).  Format a list of line numbers
750
 
    # for printing by coalescing groups of lines as long as the lines
751
 
    # represent consecutive statements.  This will coalesce even if
752
 
    # there are gaps between statements, so if statements =
753
 
    # [1,2,3,4,5,10,11,12,13,14] and lines = [1,2,5,10,11,13,14] then
754
 
    # format_lines will return "1-2, 5-11, 13-14".
755
 
 
756
 
    def format_lines(self, statements, lines):
757
 
        pairs = []
758
 
        i = 0
759
 
        j = 0
760
 
        start = None
761
 
        pairs = []
762
 
        while i < len(statements) and j < len(lines):
763
 
            if statements[i] == lines[j]:
764
 
                if start == None:
765
 
                    start = lines[j]
766
 
                end = lines[j]
767
 
                j = j + 1
768
 
            elif start:
769
 
                pairs.append((start, end))
770
 
                start = None
771
 
            i = i + 1
772
 
        if start:
773
 
            pairs.append((start, end))
774
 
        def stringify(pair):
775
 
            start, end = pair
776
 
            if start == end:
777
 
                return "%d" % start
778
 
            else:
779
 
                return "%d-%d" % (start, end)
780
 
        ret = string.join(map(stringify, pairs), ", ")
781
 
        return ret
782
 
 
783
 
    # Backward compatibility with version 1.
784
 
    def analysis(self, morf):
785
 
        f, s, _, m, mf = self.analysis2(morf)
786
 
        return f, s, m, mf
787
 
 
788
 
    def analysis2(self, morf):
789
 
        filename, statements, excluded, line_map = self.analyze_morf(morf)
790
 
        self.canonicalize_filenames()
791
 
        if not self.cexecuted.has_key(filename):
792
 
            self.cexecuted[filename] = {}
793
 
        missing = []
794
 
        for line in statements:
795
 
            lines = line_map.get(line, [line, line])
796
 
            for l in range(lines[0], lines[1]+1):
797
 
                if self.cexecuted[filename].has_key(l):
798
 
                    break
799
 
            else:
800
 
                missing.append(line)
801
 
        return (filename, statements, excluded, missing,
802
 
                self.format_lines(statements, missing))
803
 
 
804
 
    def relative_filename(self, filename):
805
 
        """ Convert filename to relative filename from self.relative_dir.
806
 
        """
807
 
        return filename.replace(self.relative_dir, "")
808
 
 
809
 
    def morf_name(self, morf):
810
 
        """ Return the name of morf as used in report.
811
 
        """
812
 
        if hasattr(morf, '__name__'):
813
 
            return morf.__name__
814
 
        else:
815
 
            return self.relative_filename(os.path.splitext(morf)[0])
816
 
 
817
 
    def filter_by_prefix(self, morfs, omit_prefixes):
818
 
        """ Return list of morfs where the morf name does not begin
819
 
            with any one of the omit_prefixes.
820
 
        """
821
 
        filtered_morfs = []
822
 
        for morf in morfs:
823
 
            for prefix in omit_prefixes:
824
 
                if self.morf_name(morf).startswith(prefix):
825
 
                    break
826
 
            else:
827
 
                filtered_morfs.append(morf)
828
 
 
829
 
        return filtered_morfs
830
 
 
831
 
    def morf_name_compare(self, x, y):
832
 
        return cmp(self.morf_name(x), self.morf_name(y))
833
 
 
834
 
    def report(self, morfs, show_missing=1, ignore_errors=0, file=None, omit_prefixes=[]):
835
 
        if not isinstance(morfs, types.ListType):
836
 
            morfs = [morfs]
837
 
        # On windows, the shell doesn't expand wildcards.  Do it here.
838
 
        globbed = []
839
 
        for morf in morfs:
840
 
            if isinstance(morf, strclass):
841
 
                globbed.extend(glob.glob(morf))
842
 
            else:
843
 
                globbed.append(morf)
844
 
        morfs = globbed
845
 
        
846
 
        morfs = self.filter_by_prefix(morfs, omit_prefixes)
847
 
        morfs.sort(self.morf_name_compare)
848
 
 
849
 
        max_name = max([5,] + map(len, map(self.morf_name, morfs)))
850
 
        fmt_name = "%%- %ds  " % max_name
851
 
        fmt_err = fmt_name + "%s: %s"
852
 
        header = fmt_name % "Name" + " Stmts   Exec  Cover"
853
 
        fmt_coverage = fmt_name + "% 6d % 6d % 5d%%"
854
 
        if show_missing:
855
 
            header = header + "   Missing"
856
 
            fmt_coverage = fmt_coverage + "   %s"
857
 
        if not file:
858
 
            file = sys.stdout
859
 
        print >>file, header
860
 
        print >>file, "-" * len(header)
861
 
        total_statements = 0
862
 
        total_executed = 0
863
 
        for morf in morfs:
864
 
            name = self.morf_name(morf)
865
 
            try:
866
 
                _, statements, _, missing, readable  = self.analysis2(morf)
867
 
                n = len(statements)
868
 
                m = n - len(missing)
869
 
                if n > 0:
870
 
                    pc = 100.0 * m / n
871
 
                else:
872
 
                    pc = 100.0
873
 
                args = (name, n, m, pc)
874
 
                if show_missing:
875
 
                    args = args + (readable,)
876
 
                print >>file, fmt_coverage % args
877
 
                total_statements = total_statements + n
878
 
                total_executed = total_executed + m
879
 
            except KeyboardInterrupt:                       #pragma: no cover
880
 
                raise
881
 
            except:
882
 
                if not ignore_errors:
883
 
                    typ, msg = sys.exc_info()[:2]
884
 
                    print >>file, fmt_err % (name, typ, msg)
885
 
        if len(morfs) > 1:
886
 
            print >>file, "-" * len(header)
887
 
            if total_statements > 0:
888
 
                pc = 100.0 * total_executed / total_statements
889
 
            else:
890
 
                pc = 100.0
891
 
            args = ("TOTAL", total_statements, total_executed, pc)
892
 
            if show_missing:
893
 
                args = args + ("",)
894
 
            print >>file, fmt_coverage % args
895
 
 
896
 
    # annotate(morfs, ignore_errors).
897
 
 
898
 
    blank_re = re.compile(r"\s*(#|$)")
899
 
    else_re = re.compile(r"\s*else\s*:\s*(#|$)")
900
 
 
901
 
    def annotate(self, morfs, directory=None, ignore_errors=0, omit_prefixes=[]):
902
 
        morfs = self.filter_by_prefix(morfs, omit_prefixes)
903
 
        for morf in morfs:
904
 
            try:
905
 
                filename, statements, excluded, missing, _ = self.analysis2(morf)
906
 
                self.annotate_file(filename, statements, excluded, missing, directory)
907
 
            except KeyboardInterrupt:
908
 
                raise
909
 
            except:
910
 
                if not ignore_errors:
911
 
                    raise
912
 
                
913
 
    def annotate_file(self, filename, statements, excluded, missing, directory=None):
914
 
        source = open(filename, 'r')
915
 
        if directory:
916
 
            dest_file = os.path.join(directory,
917
 
                                     os.path.basename(filename)
918
 
                                     + ',cover')
919
 
        else:
920
 
            dest_file = filename + ',cover'
921
 
        dest = open(dest_file, 'w')
922
 
        lineno = 0
923
 
        i = 0
924
 
        j = 0
925
 
        covered = 1
926
 
        while 1:
927
 
            line = source.readline()
928
 
            if line == '':
929
 
                break
930
 
            lineno = lineno + 1
931
 
            while i < len(statements) and statements[i] < lineno:
932
 
                i = i + 1
933
 
            while j < len(missing) and missing[j] < lineno:
934
 
                j = j + 1
935
 
            if i < len(statements) and statements[i] == lineno:
936
 
                covered = j >= len(missing) or missing[j] > lineno
937
 
            if self.blank_re.match(line):
938
 
                dest.write('  ')
939
 
            elif self.else_re.match(line):
940
 
                # Special logic for lines containing only 'else:'.  
941
 
                # See [GDR 2001-12-04b, 3.2].
942
 
                if i >= len(statements) and j >= len(missing):
943
 
                    dest.write('! ')
944
 
                elif i >= len(statements) or j >= len(missing):
945
 
                    dest.write('> ')
946
 
                elif statements[i] == missing[j]:
947
 
                    dest.write('! ')
948
 
                else:
949
 
                    dest.write('> ')
950
 
            elif lineno in excluded:
951
 
                dest.write('- ')
952
 
            elif covered:
953
 
                dest.write('> ')
954
 
            else:
955
 
                dest.write('! ')
956
 
            dest.write(line)
957
 
        source.close()
958
 
        dest.close()
959
 
 
960
 
# Singleton object.
961
 
the_coverage = coverage()
962
 
 
963
 
# Module functions call methods in the singleton object.
964
 
def use_cache(*args, **kw): 
965
 
    return the_coverage.use_cache(*args, **kw)
966
 
 
967
 
def start(*args, **kw): 
968
 
    return the_coverage.start(*args, **kw)
969
 
 
970
 
def stop(*args, **kw): 
971
 
    return the_coverage.stop(*args, **kw)
972
 
 
973
 
def erase(*args, **kw): 
974
 
    return the_coverage.erase(*args, **kw)
975
 
 
976
 
def begin_recursive(*args, **kw): 
977
 
    return the_coverage.begin_recursive(*args, **kw)
978
 
 
979
 
def end_recursive(*args, **kw): 
980
 
    return the_coverage.end_recursive(*args, **kw)
981
 
 
982
 
def exclude(*args, **kw): 
983
 
    return the_coverage.exclude(*args, **kw)
984
 
 
985
 
def analysis(*args, **kw): 
986
 
    return the_coverage.analysis(*args, **kw)
987
 
 
988
 
def analysis2(*args, **kw): 
989
 
    return the_coverage.analysis2(*args, **kw)
990
 
 
991
 
def report(*args, **kw): 
992
 
    return the_coverage.report(*args, **kw)
993
 
 
994
 
def annotate(*args, **kw): 
995
 
    return the_coverage.annotate(*args, **kw)
996
 
 
997
 
def annotate_file(*args, **kw): 
998
 
    return the_coverage.annotate_file(*args, **kw)
999
 
 
1000
 
# Save coverage data when Python exits.  (The atexit module wasn't
1001
 
# introduced until Python 2.0, so use sys.exitfunc when it's not
1002
 
# available.)
1003
 
try:
1004
 
    import atexit
1005
 
    atexit.register(the_coverage.save)
1006
 
except ImportError:
1007
 
    sys.exitfunc = the_coverage.save
1008
 
 
1009
 
def main():
1010
 
    the_coverage.command_line(sys.argv[1:])
1011
 
    
1012
 
# Command-line interface.
1013
 
if __name__ == '__main__':
1014
 
    main()
1015
 
 
1016
 
 
1017
 
# A. REFERENCES
1018
 
#
1019
 
# [GDR 2001-12-04a] "Statement coverage for Python"; Gareth Rees;
1020
 
# Ravenbrook Limited; 2001-12-04;
1021
 
# <http://www.nedbatchelder.com/code/modules/rees-coverage.html>.
1022
 
#
1023
 
# [GDR 2001-12-04b] "Statement coverage for Python: design and
1024
 
# analysis"; Gareth Rees; Ravenbrook Limited; 2001-12-04;
1025
 
# <http://www.nedbatchelder.com/code/modules/rees-design.html>.
1026
 
#
1027
 
# [van Rossum 2001-07-20a] "Python Reference Manual (releae 2.1.1)";
1028
 
# Guide van Rossum; 2001-07-20;
1029
 
# <http://www.python.org/doc/2.1.1/ref/ref.html>.
1030
 
#
1031
 
# [van Rossum 2001-07-20b] "Python Library Reference"; Guido van Rossum;
1032
 
# 2001-07-20; <http://www.python.org/doc/2.1.1/lib/lib.html>.
1033
 
#
1034
 
#
1035
 
# B. DOCUMENT HISTORY
1036
 
#
1037
 
# 2001-12-04 GDR Created.
1038
 
#
1039
 
# 2001-12-06 GDR Added command-line interface and source code
1040
 
# annotation.
1041
 
#
1042
 
# 2001-12-09 GDR Moved design and interface to separate documents.
1043
 
#
1044
 
# 2001-12-10 GDR Open cache file as binary on Windows.  Allow
1045
 
# simultaneous -e and -x, or -a and -r.
1046
 
#
1047
 
# 2001-12-12 GDR Added command-line help.  Cache analysis so that it
1048
 
# only needs to be done once when you specify -a and -r.
1049
 
#
1050
 
# 2001-12-13 GDR Improved speed while recording.  Portable between
1051
 
# Python 1.5.2 and 2.1.1.
1052
 
#
1053
 
# 2002-01-03 GDR Module-level functions work correctly.
1054
 
#
1055
 
# 2002-01-07 GDR Update sys.path when running a file with the -x option,
1056
 
# so that it matches the value the program would get if it were run on
1057
 
# its own.
1058
 
#
1059
 
# 2004-12-12 NMB Significant code changes.
1060
 
# - Finding executable statements has been rewritten so that docstrings and
1061
 
#   other quirks of Python execution aren't mistakenly identified as missing
1062
 
#   lines.
1063
 
# - Lines can be excluded from consideration, even entire suites of lines.
1064
 
# - The filesystem cache of covered lines can be disabled programmatically.
1065
 
# - Modernized the code.
1066
 
#
1067
 
# 2004-12-14 NMB Minor tweaks.  Return 'analysis' to its original behavior
1068
 
# and add 'analysis2'.  Add a global for 'annotate', and factor it, adding
1069
 
# 'annotate_file'.
1070
 
#
1071
 
# 2004-12-31 NMB Allow for keyword arguments in the module global functions.
1072
 
# Thanks, Allen.
1073
 
#
1074
 
# 2005-12-02 NMB Call threading.settrace so that all threads are measured.
1075
 
# Thanks Martin Fuzzey. Add a file argument to report so that reports can be 
1076
 
# captured to a different destination.
1077
 
#
1078
 
# 2005-12-03 NMB coverage.py can now measure itself.
1079
 
#
1080
 
# 2005-12-04 NMB Adapted Greg Rogers' patch for using relative filenames,
1081
 
# and sorting and omitting files to report on.
1082
 
#
1083
 
# 2006-07-23 NMB Applied Joseph Tate's patch for function decorators.
1084
 
#
1085
 
# 2006-08-21 NMB Applied Sigve Tjora and Mark van der Wal's fixes for argument
1086
 
# handling.
1087
 
#
1088
 
# 2006-08-22 NMB Applied Geoff Bache's parallel mode patch.
1089
 
#
1090
 
# 2006-08-23 NMB Refactorings to improve testability.  Fixes to command-line
1091
 
# logic for parallel mode and collect.
1092
 
#
1093
 
# 2006-08-25 NMB "#pragma: nocover" is excluded by default.
1094
 
#
1095
 
# 2006-09-10 NMB Properly ignore docstrings and other constant expressions that
1096
 
# appear in the middle of a function, a problem reported by Tim Leslie.
1097
 
# Minor changes to avoid lint warnings.
1098
 
#
1099
 
# 2006-09-17 NMB coverage.erase() shouldn't clobber the exclude regex.
1100
 
# Change how parallel mode is invoked, and fix erase() so that it erases the
1101
 
# cache when called programmatically.
1102
 
#
1103
 
# 2007-07-21 NMB In reports, ignore code executed from strings, since we can't
1104
 
# do anything useful with it anyway.
1105
 
# Better file handling on Linux, thanks Guillaume Chazarain.
1106
 
# Better shell support on Windows, thanks Noel O'Boyle.
1107
 
# Python 2.2 support maintained, thanks Catherine Proulx.
1108
 
#
1109
 
# 2007-07-22 NMB Python 2.5 now fully supported. The method of dealing with
1110
 
# multi-line statements is now less sensitive to the exact line that Python
1111
 
# reports during execution. Pass statements are handled specially so that their
1112
 
# disappearance during execution won't throw off the measurement.
1113
 
#
1114
 
# 2007-07-23 NMB Now Python 2.5 is *really* fully supported: the body of the
1115
 
# new with statement is counted as executable.
1116
 
#
1117
 
# 2007-07-29 NMB Better packaging.
1118
 
#
1119
 
# 2007-09-30 NMB Don't try to predict whether a file is Python source based on
1120
 
# the extension. Extensionless files are often Pythons scripts. Instead, simply
1121
 
# parse the file and catch the syntax errors.  Hat tip to Ben Finney.
1122
 
#
1123
 
# 2008-05-25 NMB Open files in rU mode to avoid line ending craziness.
1124
 
# Thanks, Edward Loper.
1125
 
#
1126
 
# 2008-09-14 NMB Add support for finding source files in eggs.
1127
 
# Don't check for morf's being instances of ModuleType, instead use duck typing
1128
 
# so that pseudo-modules can participate. Thanks, Imri Goldberg.
1129
 
# Use os.realpath as part of the fixing of filenames so that symlinks won't
1130
 
# confuse things.  Thanks, Patrick Mezard.
1131
 
#
1132
 
#
1133
 
# C. COPYRIGHT AND LICENCE
1134
 
#
1135
 
# Copyright 2001 Gareth Rees.  All rights reserved.
1136
 
# Copyright 2004-2008 Ned Batchelder.  All rights reserved.
1137
 
#
1138
 
# Redistribution and use in source and binary forms, with or without
1139
 
# modification, are permitted provided that the following conditions are
1140
 
# met:
1141
 
#
1142
 
# 1. Redistributions of source code must retain the above copyright
1143
 
#    notice, this list of conditions and the following disclaimer.
1144
 
#
1145
 
# 2. Redistributions in binary form must reproduce the above copyright
1146
 
#    notice, this list of conditions and the following disclaimer in the
1147
 
#    documentation and/or other materials provided with the
1148
 
#    distribution.
1149
 
#
1150
 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1151
 
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1152
 
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1153
 
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1154
 
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
1155
 
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1156
 
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
1157
 
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
1158
 
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1159
 
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1160
 
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1161
 
# DAMAGE.
1162
 
#
1163
 
# $Id: coverage.py 100 2008-10-12 12:08:22Z nedbat $