summaryrefslogtreecommitdiff
path: root/coverage/cmdline.py
blob: 6086c62d2831c2127c4b4234ddfd42c7cf4885dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
"""Command-line support for Coverage."""

import getopt, sys

from coverage.execfile import run_python_file

USAGE = r"""
Coverage version %(__version__)s

Usage:

coverage -x [-p] [-L] MODULE.py [ARG1 ARG2 ...]
    Execute the module, passing the given command-line arguments, collecting
    coverage data.  With the -p option, include the machine name and process
    ID in the .coverage file name.  With -L, measure coverage even inside the
    Python standard library, which isn't done by default.

coverage -e
    Erase collected coverage data.

coverage -c
    Combine data from multiple coverage files (as created by -p option above)
    and store it into a single file representing the union of the coverage.

coverage -r [-m] [-i] [-o DIR,...] [FILE1 FILE2 ...]
    Report on the statement coverage for the given files.  With the -m
    option, show line numbers of the statements that weren't executed.

coverage -b -d DIR [-i] [-o DIR,...] [FILE1 FILE2 ...]
    Create an HTML report of the coverage of the given files.  Each file gets
    its own page, with the file listing decorated to show executed, excluded,
    and missed lines.

coverage -a [-d DIR] [-i] [-o DIR,...] [FILE1 FILE2 ...]
    Make annotated copies of the given files, marking statements that
    are executed with > and statements that are missed with !.

-d DIR
    Write output files for -b or -a to this directory.

-i  Ignore errors while reporting or annotating.

-o DIR,...
    Omit reporting or annotating files when their filename path starts with
    a directory listed in the omit list.
    e.g. coverage -i -r -o c:\python25,lib\enthought\traits

-h  Print this help.

Coverage data is saved in the file .coverage by default.  Set the
COVERAGE_FILE environment variable to save it somewhere else.
""".strip()


class CoverageScript:
    """The command-line interface to Coverage."""
    
    def __init__(self):
        import coverage
        self.covpkg = coverage

    def help(self, error=None):
        """Display an error message, or the usage for Coverage."""
        if error:
            print error
            print "Use -h for help."
        else:
            print USAGE % self.covpkg.__dict__

    def command_line(self, argv, help_fn=None):
        """The bulk of the command line interface to Coverage.
        
        `argv` is the argument list to process.
        `help_fn` is the help function to use when something goes wrong.
        
        """
        # Collect the command-line options.
        help_fn = help_fn or self.help
        OK, ERR = 0, 1
        settings = {}
        optmap = {
            '-a': 'annotate',
            '-b': 'html',
            '-c': 'combine',
            '-d:': 'directory=',
            '-e': 'erase',
            '-h': 'help',
            '-i': 'ignore-errors',
            '-L': 'stdlib',
            '-m': 'show-missing',
            '-p': 'parallel-mode',
            '-r': 'report',
            '-x': 'execute',
            '-o:': 'omit=',
            }
        short_opts = ''.join(map(lambda o: o[1:], optmap.keys()))
        long_opts = optmap.values()
        options, args = getopt.getopt(argv, short_opts, long_opts)
        for o, a in options:
            if optmap.has_key(o):
                settings[optmap[o]] = True
            elif optmap.has_key(o + ':'):
                settings[optmap[o + ':']] = a
            elif o[2:] in long_opts:
                settings[o[2:]] = True
            elif o[2:] + '=' in long_opts:
                settings[o[2:]+'='] = a

        if settings.get('help'):
            help_fn()
            return OK

        # Check for conflicts and problems in the options.
        for i in ['erase', 'execute']:
            for j in ['annotate', 'html', 'report', 'combine']:
                if settings.get(i) and settings.get(j):
                    help_fn("You can't specify the '%s' and '%s' "
                              "options at the same time." % (i, j))
                    return ERR

        args_needed = (settings.get('execute')
                       or settings.get('annotate')
                       or settings.get('html')
                       or settings.get('report'))
        action = (settings.get('erase') 
                  or settings.get('combine')
                  or args_needed)
        if not action:
            help_fn(
                "You must specify at least one of -e, -x, -c, -r, -a, or -b."
                )
            return ERR
        if not args_needed and args:
            help_fn("Unexpected arguments: %s" % " ".join(args))
            return ERR
        
        # Do something.
        self.coverage = self.covpkg.coverage(
            parallel_mode = settings.get('parallel-mode'),
            cover_stdlib = settings.get('stdlib')
            )
        self.coverage.get_ready()

        if settings.get('erase'):
            self.coverage.erase()
        
        if settings.get('execute'):
            if not args:
                help_fn("Nothing to do.")
                return ERR
            
            # Run the script.
            self.coverage.start()
            try:
                run_python_file(args[0], args)
            finally:
                self.coverage.stop()
        
        if settings.get('combine'):
            self.coverage.combine()

        # Remaining actions are reporting, with some common options.
        show_missing = settings.get('show-missing')
        directory = settings.get('directory=')
        report_args = {
            'morfs': args,
            'ignore_errors': settings.get('ignore-errors'),
            }

        omit = settings.get('omit=')
        if omit:
            omit = omit.split(',')
        report_args['omit_prefixes'] = omit
        
        if settings.get('report'):
            self.coverage.report(show_missing=show_missing, **report_args)
        if settings.get('annotate'):
            self.coverage.annotate(directory=directory, **report_args)
        if settings.get('html'):
            self.coverage.html_report(directory=directory, **report_args)

        return OK
    

def main():
    """The main entrypoint to Coverage.
    
    This is installed as the script entrypoint.
    
    """
    return CoverageScript().command_line(sys.argv[1:])