5 |
5 |
6 import glob |
6 import glob |
7 import optparse |
7 import optparse |
8 import os.path |
8 import os.path |
9 import sys |
9 import sys |
|
10 import textwrap |
10 import traceback |
11 import traceback |
11 |
12 |
12 from coverage import env |
13 from coverage import env |
|
14 from coverage.collector import CTracer |
13 from coverage.execfile import run_python_file, run_python_module |
15 from coverage.execfile import run_python_file, run_python_module |
14 from coverage.misc import CoverageException, ExceptionDuringRun, NoSource |
16 from coverage.misc import CoverageException, ExceptionDuringRun, NoSource |
15 from coverage.debug import info_formatter, info_header |
17 from coverage.debug import info_formatter, info_header |
16 |
18 |
17 |
19 |
18 class Opts(object): |
20 class Opts(object): |
19 """A namespace class for individual options we'll build parsers from.""" |
21 """A namespace class for individual options we'll build parsers from.""" |
20 |
22 |
21 append = optparse.make_option( |
23 append = optparse.make_option( |
22 '-a', '--append', action='store_true', |
24 '-a', '--append', action='store_true', |
23 help="Append coverage data to .coverage, otherwise it is started " |
25 help="Append coverage data to .coverage, otherwise it is started clean with each run.", |
24 "clean with each run." |
26 ) |
25 ) |
|
26 branch = optparse.make_option( |
27 branch = optparse.make_option( |
27 '', '--branch', action='store_true', |
28 '', '--branch', action='store_true', |
28 help="Measure branch coverage in addition to statement coverage." |
29 help="Measure branch coverage in addition to statement coverage.", |
29 ) |
30 ) |
30 CONCURRENCY_CHOICES = [ |
31 CONCURRENCY_CHOICES = [ |
31 "thread", "gevent", "greenlet", "eventlet", "multiprocessing", |
32 "thread", "gevent", "greenlet", "eventlet", "multiprocessing", |
32 ] |
33 ] |
33 concurrency = optparse.make_option( |
34 concurrency = optparse.make_option( |
34 '', '--concurrency', action='store', metavar="LIB", |
35 '', '--concurrency', action='store', metavar="LIB", |
35 choices=CONCURRENCY_CHOICES, |
36 choices=CONCURRENCY_CHOICES, |
36 help="Properly measure code using a concurrency library. " |
37 help=( |
37 "Valid values are: %s." % ", ".join(CONCURRENCY_CHOICES) |
38 "Properly measure code using a concurrency library. " |
38 ) |
39 "Valid values are: %s." |
|
40 ) % ", ".join(CONCURRENCY_CHOICES), |
|
41 ) |
39 debug = optparse.make_option( |
42 debug = optparse.make_option( |
40 '', '--debug', action='store', metavar="OPTS", |
43 '', '--debug', action='store', metavar="OPTS", |
41 help="Debug options, separated by commas" |
44 help="Debug options, separated by commas", |
42 ) |
45 ) |
43 directory = optparse.make_option( |
46 directory = optparse.make_option( |
44 '-d', '--directory', action='store', metavar="DIR", |
47 '-d', '--directory', action='store', metavar="DIR", |
45 help="Write the output files to DIR." |
48 help="Write the output files to DIR.", |
46 ) |
49 ) |
47 fail_under = optparse.make_option( |
50 fail_under = optparse.make_option( |
48 '', '--fail-under', action='store', metavar="MIN", type="int", |
51 '', '--fail-under', action='store', metavar="MIN", type="int", |
49 help="Exit with a status of 2 if the total coverage is less than MIN." |
52 help="Exit with a status of 2 if the total coverage is less than MIN.", |
50 ) |
53 ) |
51 help = optparse.make_option( |
54 help = optparse.make_option( |
52 '-h', '--help', action='store_true', |
55 '-h', '--help', action='store_true', |
53 help="Get help on this command." |
56 help="Get help on this command.", |
54 ) |
57 ) |
55 ignore_errors = optparse.make_option( |
58 ignore_errors = optparse.make_option( |
56 '-i', '--ignore-errors', action='store_true', |
59 '-i', '--ignore-errors', action='store_true', |
57 help="Ignore errors while reading source files." |
60 help="Ignore errors while reading source files.", |
58 ) |
61 ) |
59 include = optparse.make_option( |
62 include = optparse.make_option( |
60 '', '--include', action='store', |
63 '', '--include', action='store', |
61 metavar="PAT1,PAT2,...", |
64 metavar="PAT1,PAT2,...", |
62 help="Include only files whose paths match one of these patterns. " |
65 help=( |
63 "Accepts shell-style wildcards, which must be quoted." |
66 "Include only files whose paths match one of these patterns. " |
64 ) |
67 "Accepts shell-style wildcards, which must be quoted." |
|
68 ), |
|
69 ) |
65 pylib = optparse.make_option( |
70 pylib = optparse.make_option( |
66 '-L', '--pylib', action='store_true', |
71 '-L', '--pylib', action='store_true', |
67 help="Measure coverage even inside the Python installed library, " |
72 help=( |
68 "which isn't done by default." |
73 "Measure coverage even inside the Python installed library, " |
69 ) |
74 "which isn't done by default." |
|
75 ), |
|
76 ) |
70 show_missing = optparse.make_option( |
77 show_missing = optparse.make_option( |
71 '-m', '--show-missing', action='store_true', |
78 '-m', '--show-missing', action='store_true', |
72 help="Show line numbers of statements in each module that weren't " |
79 help="Show line numbers of statements in each module that weren't executed.", |
73 "executed." |
80 ) |
74 ) |
|
75 skip_covered = optparse.make_option( |
81 skip_covered = optparse.make_option( |
76 '--skip-covered', action='store_true', |
82 '--skip-covered', action='store_true', |
77 help="Skip files with 100% coverage." |
83 help="Skip files with 100% coverage.", |
78 ) |
84 ) |
79 omit = optparse.make_option( |
85 omit = optparse.make_option( |
80 '', '--omit', action='store', |
86 '', '--omit', action='store', |
81 metavar="PAT1,PAT2,...", |
87 metavar="PAT1,PAT2,...", |
82 help="Omit files whose paths match one of these patterns. " |
88 help=( |
83 "Accepts shell-style wildcards, which must be quoted." |
89 "Omit files whose paths match one of these patterns. " |
84 ) |
90 "Accepts shell-style wildcards, which must be quoted." |
|
91 ), |
|
92 ) |
85 output_xml = optparse.make_option( |
93 output_xml = optparse.make_option( |
86 '-o', '', action='store', dest="outfile", |
94 '-o', '', action='store', dest="outfile", |
87 metavar="OUTFILE", |
95 metavar="OUTFILE", |
88 help="Write the XML report to this file. Defaults to 'coverage.xml'" |
96 help="Write the XML report to this file. Defaults to 'coverage.xml'", |
89 ) |
97 ) |
90 parallel_mode = optparse.make_option( |
98 parallel_mode = optparse.make_option( |
91 '-p', '--parallel-mode', action='store_true', |
99 '-p', '--parallel-mode', action='store_true', |
92 help="Append the machine name, process id and random number to the " |
100 help=( |
93 ".coverage data file name to simplify collecting data from " |
101 "Append the machine name, process id and random number to the " |
94 "many processes." |
102 ".coverage data file name to simplify collecting data from " |
95 ) |
103 "many processes." |
|
104 ), |
|
105 ) |
96 module = optparse.make_option( |
106 module = optparse.make_option( |
97 '-m', '--module', action='store_true', |
107 '-m', '--module', action='store_true', |
98 help="<pyfile> is an importable Python module, not a script path, " |
108 help=( |
99 "to be run as 'python -m' would run it." |
109 "<pyfile> is an importable Python module, not a script path, " |
100 ) |
110 "to be run as 'python -m' would run it." |
|
111 ), |
|
112 ) |
101 rcfile = optparse.make_option( |
113 rcfile = optparse.make_option( |
102 '', '--rcfile', action='store', |
114 '', '--rcfile', action='store', |
103 help="Specify configuration file. Defaults to '.coveragerc'" |
115 help="Specify configuration file. Defaults to '.coveragerc'", |
104 ) |
116 ) |
105 source = optparse.make_option( |
117 source = optparse.make_option( |
106 '', '--source', action='store', metavar="SRC1,SRC2,...", |
118 '', '--source', action='store', metavar="SRC1,SRC2,...", |
107 help="A list of packages or directories of code to be measured." |
119 help="A list of packages or directories of code to be measured.", |
108 ) |
120 ) |
109 timid = optparse.make_option( |
121 timid = optparse.make_option( |
110 '', '--timid', action='store_true', |
122 '', '--timid', action='store_true', |
111 help="Use a simpler but slower trace method. Try this if you get " |
123 help=( |
112 "seemingly impossible results!" |
124 "Use a simpler but slower trace method. Try this if you get " |
113 ) |
125 "seemingly impossible results!" |
|
126 ), |
|
127 ) |
114 title = optparse.make_option( |
128 title = optparse.make_option( |
115 '', '--title', action='store', metavar="TITLE", |
129 '', '--title', action='store', metavar="TITLE", |
116 help="A text string to use as the title on the HTML." |
130 help="A text string to use as the title on the HTML.", |
117 ) |
131 ) |
118 version = optparse.make_option( |
132 version = optparse.make_option( |
119 '', '--version', action='store_true', |
133 '', '--version', action='store_true', |
120 help="Display version information and exit." |
134 help="Display version information and exit.", |
121 ) |
135 ) |
122 |
136 |
123 |
137 |
124 class CoverageOptionParser(optparse.OptionParser, object): |
138 class CoverageOptionParser(optparse.OptionParser, object): |
125 """Base OptionParser for coverage.py. |
139 """Base OptionParser for coverage.py. |
126 |
140 |
229 def __eq__(self, other): |
240 def __eq__(self, other): |
230 # A convenience equality, so that I can put strings in unit test |
241 # A convenience equality, so that I can put strings in unit test |
231 # results, and they will compare equal to objects. |
242 # results, and they will compare equal to objects. |
232 return (other == "<CmdOptionParser:%s>" % self.cmd) |
243 return (other == "<CmdOptionParser:%s>" % self.cmd) |
233 |
244 |
|
245 def get_prog_name(self): |
|
246 """Override of an undocumented function in optparse.OptionParser.""" |
|
247 program_name = super(CmdOptionParser, self).get_prog_name() |
|
248 |
|
249 # Include the sub-command for this parser as part of the command. |
|
250 return "%(command)s %(subcommand)s" % {'command': program_name, 'subcommand': self.cmd} |
|
251 |
|
252 |
234 GLOBAL_ARGS = [ |
253 GLOBAL_ARGS = [ |
235 Opts.debug, |
254 Opts.debug, |
236 Opts.help, |
255 Opts.help, |
237 Opts.rcfile, |
256 Opts.rcfile, |
238 ] |
257 ] |
239 |
258 |
240 CMDS = { |
259 CMDS = { |
241 'annotate': CmdOptionParser("annotate", |
260 'annotate': CmdOptionParser( |
|
261 "annotate", |
242 [ |
262 [ |
243 Opts.directory, |
263 Opts.directory, |
244 Opts.ignore_errors, |
264 Opts.ignore_errors, |
245 Opts.include, |
265 Opts.include, |
246 Opts.omit, |
266 Opts.omit, |
247 ] + GLOBAL_ARGS, |
267 ] + GLOBAL_ARGS, |
248 usage = "[options] [modules]", |
268 usage="[options] [modules]", |
249 description = "Make annotated copies of the given files, marking " |
269 description=( |
250 "statements that are executed with > and statements that are " |
270 "Make annotated copies of the given files, marking statements that are executed " |
251 "missed with !." |
271 "with > and statements that are missed with !." |
252 ), |
272 ), |
253 |
273 ), |
254 'combine': CmdOptionParser("combine", GLOBAL_ARGS, |
274 |
255 usage = "<path1> <path2> ... <pathN>", |
275 'combine': CmdOptionParser( |
256 description = "Combine data from multiple coverage files collected " |
276 "combine", |
|
277 GLOBAL_ARGS, |
|
278 usage="<path1> <path2> ... <pathN>", |
|
279 description=( |
|
280 "Combine data from multiple coverage files collected " |
257 "with 'run -p'. The combined results are written to a single " |
281 "with 'run -p'. The combined results are written to a single " |
258 "file representing the union of the data. The positional " |
282 "file representing the union of the data. The positional " |
259 "arguments are data files or directories containing data files. " |
283 "arguments are data files or directories containing data files. " |
260 "If no paths are provided, data files in the default data file's " |
284 "If no paths are provided, data files in the default data file's " |
261 "directory are combined." |
285 "directory are combined." |
262 ), |
286 ), |
263 |
287 ), |
264 'debug': CmdOptionParser("debug", GLOBAL_ARGS, |
288 |
265 usage = "<topic>", |
289 'debug': CmdOptionParser( |
266 description = "Display information on the internals of coverage.py, " |
290 "debug", GLOBAL_ARGS, |
|
291 usage="<topic>", |
|
292 description=( |
|
293 "Display information on the internals of coverage.py, " |
267 "for diagnosing problems. " |
294 "for diagnosing problems. " |
268 "Topics are 'data' to show a summary of the collected data, " |
295 "Topics are 'data' to show a summary of the collected data, " |
269 "or 'sys' to show installation information." |
296 "or 'sys' to show installation information." |
270 ), |
297 ), |
271 |
298 ), |
272 'erase': CmdOptionParser("erase", GLOBAL_ARGS, |
299 |
273 usage = " ", |
300 'erase': CmdOptionParser( |
274 description = "Erase previously collected coverage data." |
301 "erase", GLOBAL_ARGS, |
275 ), |
302 usage=" ", |
276 |
303 description="Erase previously collected coverage data.", |
277 'help': CmdOptionParser("help", GLOBAL_ARGS, |
304 ), |
278 usage = "[command]", |
305 |
279 description = "Describe how to use coverage.py" |
306 'help': CmdOptionParser( |
280 ), |
307 "help", GLOBAL_ARGS, |
281 |
308 usage="[command]", |
282 'html': CmdOptionParser("html", |
309 description="Describe how to use coverage.py", |
|
310 ), |
|
311 |
|
312 'html': CmdOptionParser( |
|
313 "html", |
283 [ |
314 [ |
284 Opts.directory, |
315 Opts.directory, |
285 Opts.fail_under, |
316 Opts.fail_under, |
286 Opts.ignore_errors, |
317 Opts.ignore_errors, |
287 Opts.include, |
318 Opts.include, |
288 Opts.omit, |
319 Opts.omit, |
289 Opts.title, |
320 Opts.title, |
290 ] + GLOBAL_ARGS, |
321 ] + GLOBAL_ARGS, |
291 usage = "[options] [modules]", |
322 usage="[options] [modules]", |
292 description = "Create an HTML report of the coverage of the files. " |
323 description=( |
|
324 "Create an HTML report of the coverage of the files. " |
293 "Each file gets its own page, with the source decorated to show " |
325 "Each file gets its own page, with the source decorated to show " |
294 "executed, excluded, and missed lines." |
326 "executed, excluded, and missed lines." |
295 ), |
327 ), |
296 |
328 ), |
297 'report': CmdOptionParser("report", |
329 |
|
330 'report': CmdOptionParser( |
|
331 "report", |
298 [ |
332 [ |
299 Opts.fail_under, |
333 Opts.fail_under, |
300 Opts.ignore_errors, |
334 Opts.ignore_errors, |
301 Opts.include, |
335 Opts.include, |
302 Opts.omit, |
336 Opts.omit, |
303 Opts.show_missing, |
337 Opts.show_missing, |
304 Opts.skip_covered, |
338 Opts.skip_covered, |
305 ] + GLOBAL_ARGS, |
339 ] + GLOBAL_ARGS, |
306 usage = "[options] [modules]", |
340 usage="[options] [modules]", |
307 description = "Report coverage statistics on modules." |
341 description="Report coverage statistics on modules." |
308 ), |
342 ), |
309 |
343 |
310 'run': CmdOptionParser("run", |
344 'run': CmdOptionParser( |
|
345 "run", |
311 [ |
346 [ |
312 Opts.append, |
347 Opts.append, |
313 Opts.branch, |
348 Opts.branch, |
314 Opts.concurrency, |
349 Opts.concurrency, |
315 Opts.include, |
350 Opts.include, |