OLD | NEW |
| (Empty) |
1 """Command-line support for Coverage.""" | |
2 | |
3 import optparse, sys, traceback | |
4 | |
5 from coverage.backward import sorted # pylint: disable=W0622 | |
6 from coverage.execfile import run_python_file, run_python_module | |
7 from coverage.misc import CoverageException, ExceptionDuringRun, NoSource | |
8 | |
9 | |
10 class Opts(object): | |
11 """A namespace class for individual options we'll build parsers from.""" | |
12 | |
13 append = optparse.make_option( | |
14 '-a', '--append', action='store_false', dest="erase_first", | |
15 help="Append coverage data to .coverage, otherwise it is started " | |
16 "clean with each run." | |
17 ) | |
18 branch = optparse.make_option( | |
19 '', '--branch', action='store_true', | |
20 help="Measure branch coverage in addition to statement coverage." | |
21 ) | |
22 directory = optparse.make_option( | |
23 '-d', '--directory', action='store', metavar="DIR", | |
24 help="Write the output files to DIR." | |
25 ) | |
26 fail_under = optparse.make_option( | |
27 '', '--fail-under', action='store', metavar="MIN", type="int", | |
28 help="Exit with a status of 2 if the total coverage is less than MIN." | |
29 ) | |
30 help = optparse.make_option( | |
31 '-h', '--help', action='store_true', | |
32 help="Get help on this command." | |
33 ) | |
34 ignore_errors = optparse.make_option( | |
35 '-i', '--ignore-errors', action='store_true', | |
36 help="Ignore errors while reading source files." | |
37 ) | |
38 include = optparse.make_option( | |
39 '', '--include', action='store', | |
40 metavar="PAT1,PAT2,...", | |
41 help="Include files only when their filename path matches one of " | |
42 "these patterns. Usually needs quoting on the command line." | |
43 ) | |
44 pylib = optparse.make_option( | |
45 '-L', '--pylib', action='store_true', | |
46 help="Measure coverage even inside the Python installed library, " | |
47 "which isn't done by default." | |
48 ) | |
49 show_missing = optparse.make_option( | |
50 '-m', '--show-missing', action='store_true', | |
51 help="Show line numbers of statements in each module that weren't " | |
52 "executed." | |
53 ) | |
54 old_omit = optparse.make_option( | |
55 '-o', '--omit', action='store', | |
56 metavar="PAT1,PAT2,...", | |
57 help="Omit files when their filename matches one of these patterns. " | |
58 "Usually needs quoting on the command line." | |
59 ) | |
60 omit = optparse.make_option( | |
61 '', '--omit', action='store', | |
62 metavar="PAT1,PAT2,...", | |
63 help="Omit files when their filename matches one of these patterns. " | |
64 "Usually needs quoting on the command line." | |
65 ) | |
66 output_xml = optparse.make_option( | |
67 '-o', '', action='store', dest="outfile", | |
68 metavar="OUTFILE", | |
69 help="Write the XML report to this file. Defaults to 'coverage.xml'" | |
70 ) | |
71 parallel_mode = optparse.make_option( | |
72 '-p', '--parallel-mode', action='store_true', | |
73 help="Append the machine name, process id and random number to the " | |
74 ".coverage data file name to simplify collecting data from " | |
75 "many processes." | |
76 ) | |
77 module = optparse.make_option( | |
78 '-m', '--module', action='store_true', | |
79 help="<pyfile> is an importable Python module, not a script path, " | |
80 "to be run as 'python -m' would run it." | |
81 ) | |
82 rcfile = optparse.make_option( | |
83 '', '--rcfile', action='store', | |
84 help="Specify configuration file. Defaults to '.coveragerc'" | |
85 ) | |
86 source = optparse.make_option( | |
87 '', '--source', action='store', metavar="SRC1,SRC2,...", | |
88 help="A list of packages or directories of code to be measured." | |
89 ) | |
90 timid = optparse.make_option( | |
91 '', '--timid', action='store_true', | |
92 help="Use a simpler but slower trace method. Try this if you get " | |
93 "seemingly impossible results!" | |
94 ) | |
95 title = optparse.make_option( | |
96 '', '--title', action='store', metavar="TITLE", | |
97 help="A text string to use as the title on the HTML." | |
98 ) | |
99 version = optparse.make_option( | |
100 '', '--version', action='store_true', | |
101 help="Display version information and exit." | |
102 ) | |
103 | |
104 | |
105 class CoverageOptionParser(optparse.OptionParser, object): | |
106 """Base OptionParser for coverage. | |
107 | |
108 Problems don't exit the program. | |
109 Defaults are initialized for all options. | |
110 | |
111 """ | |
112 | |
113 def __init__(self, *args, **kwargs): | |
114 super(CoverageOptionParser, self).__init__( | |
115 add_help_option=False, *args, **kwargs | |
116 ) | |
117 self.set_defaults( | |
118 actions=[], | |
119 branch=None, | |
120 directory=None, | |
121 fail_under=None, | |
122 help=None, | |
123 ignore_errors=None, | |
124 include=None, | |
125 omit=None, | |
126 parallel_mode=None, | |
127 module=None, | |
128 pylib=None, | |
129 rcfile=True, | |
130 show_missing=None, | |
131 source=None, | |
132 timid=None, | |
133 title=None, | |
134 erase_first=None, | |
135 version=None, | |
136 ) | |
137 | |
138 self.disable_interspersed_args() | |
139 self.help_fn = self.help_noop | |
140 | |
141 def help_noop(self, error=None, topic=None, parser=None): | |
142 """No-op help function.""" | |
143 pass | |
144 | |
145 class OptionParserError(Exception): | |
146 """Used to stop the optparse error handler ending the process.""" | |
147 pass | |
148 | |
149 def parse_args(self, args=None, options=None): | |
150 """Call optparse.parse_args, but return a triple: | |
151 | |
152 (ok, options, args) | |
153 | |
154 """ | |
155 try: | |
156 options, args = \ | |
157 super(CoverageOptionParser, self).parse_args(args, options) | |
158 except self.OptionParserError: | |
159 return False, None, None | |
160 return True, options, args | |
161 | |
162 def error(self, msg): | |
163 """Override optparse.error so sys.exit doesn't get called.""" | |
164 self.help_fn(msg) | |
165 raise self.OptionParserError | |
166 | |
167 | |
168 class ClassicOptionParser(CoverageOptionParser): | |
169 """Command-line parser for coverage.py classic arguments.""" | |
170 | |
171 def __init__(self): | |
172 super(ClassicOptionParser, self).__init__() | |
173 | |
174 self.add_action('-a', '--annotate', 'annotate') | |
175 self.add_action('-b', '--html', 'html') | |
176 self.add_action('-c', '--combine', 'combine') | |
177 self.add_action('-e', '--erase', 'erase') | |
178 self.add_action('-r', '--report', 'report') | |
179 self.add_action('-x', '--execute', 'execute') | |
180 | |
181 self.add_options([ | |
182 Opts.directory, | |
183 Opts.help, | |
184 Opts.ignore_errors, | |
185 Opts.pylib, | |
186 Opts.show_missing, | |
187 Opts.old_omit, | |
188 Opts.parallel_mode, | |
189 Opts.timid, | |
190 Opts.version, | |
191 ]) | |
192 | |
193 def add_action(self, dash, dashdash, action_code): | |
194 """Add a specialized option that is the action to execute.""" | |
195 option = self.add_option(dash, dashdash, action='callback', | |
196 callback=self._append_action | |
197 ) | |
198 option.action_code = action_code | |
199 | |
200 def _append_action(self, option, opt_unused, value_unused, parser): | |
201 """Callback for an option that adds to the `actions` list.""" | |
202 parser.values.actions.append(option.action_code) | |
203 | |
204 | |
205 class CmdOptionParser(CoverageOptionParser): | |
206 """Parse one of the new-style commands for coverage.py.""" | |
207 | |
208 def __init__(self, action, options=None, defaults=None, usage=None, | |
209 cmd=None, description=None | |
210 ): | |
211 """Create an OptionParser for a coverage command. | |
212 | |
213 `action` is the slug to put into `options.actions`. | |
214 `options` is a list of Option's for the command. | |
215 `defaults` is a dict of default value for options. | |
216 `usage` is the usage string to display in help. | |
217 `cmd` is the command name, if different than `action`. | |
218 `description` is the description of the command, for the help text. | |
219 | |
220 """ | |
221 if usage: | |
222 usage = "%prog " + usage | |
223 super(CmdOptionParser, self).__init__( | |
224 prog="coverage %s" % (cmd or action), | |
225 usage=usage, | |
226 description=description, | |
227 ) | |
228 self.set_defaults(actions=[action], **(defaults or {})) | |
229 if options: | |
230 self.add_options(options) | |
231 self.cmd = cmd or action | |
232 | |
233 def __eq__(self, other): | |
234 # A convenience equality, so that I can put strings in unit test | |
235 # results, and they will compare equal to objects. | |
236 return (other == "<CmdOptionParser:%s>" % self.cmd) | |
237 | |
238 GLOBAL_ARGS = [ | |
239 Opts.rcfile, | |
240 Opts.help, | |
241 ] | |
242 | |
243 CMDS = { | |
244 'annotate': CmdOptionParser("annotate", | |
245 [ | |
246 Opts.directory, | |
247 Opts.ignore_errors, | |
248 Opts.omit, | |
249 Opts.include, | |
250 ] + GLOBAL_ARGS, | |
251 usage = "[options] [modules]", | |
252 description = "Make annotated copies of the given files, marking " | |
253 "statements that are executed with > and statements that are " | |
254 "missed with !." | |
255 ), | |
256 | |
257 'combine': CmdOptionParser("combine", GLOBAL_ARGS, | |
258 usage = " ", | |
259 description = "Combine data from multiple coverage files collected " | |
260 "with 'run -p'. The combined results are written to a single " | |
261 "file representing the union of the data." | |
262 ), | |
263 | |
264 'debug': CmdOptionParser("debug", GLOBAL_ARGS, | |
265 usage = "<topic>", | |
266 description = "Display information on the internals of coverage.py, " | |
267 "for diagnosing problems. " | |
268 "Topics are 'data' to show a summary of the collected data, " | |
269 "or 'sys' to show installation information." | |
270 ), | |
271 | |
272 'erase': CmdOptionParser("erase", GLOBAL_ARGS, | |
273 usage = " ", | |
274 description = "Erase previously collected coverage data." | |
275 ), | |
276 | |
277 'help': CmdOptionParser("help", GLOBAL_ARGS, | |
278 usage = "[command]", | |
279 description = "Describe how to use coverage.py" | |
280 ), | |
281 | |
282 'html': CmdOptionParser("html", | |
283 [ | |
284 Opts.directory, | |
285 Opts.fail_under, | |
286 Opts.ignore_errors, | |
287 Opts.omit, | |
288 Opts.include, | |
289 Opts.title, | |
290 ] + GLOBAL_ARGS, | |
291 usage = "[options] [modules]", | |
292 description = "Create an HTML report of the coverage of the files. " | |
293 "Each file gets its own page, with the source decorated to show " | |
294 "executed, excluded, and missed lines." | |
295 ), | |
296 | |
297 'report': CmdOptionParser("report", | |
298 [ | |
299 Opts.fail_under, | |
300 Opts.ignore_errors, | |
301 Opts.omit, | |
302 Opts.include, | |
303 Opts.show_missing, | |
304 ] + GLOBAL_ARGS, | |
305 usage = "[options] [modules]", | |
306 description = "Report coverage statistics on modules." | |
307 ), | |
308 | |
309 'run': CmdOptionParser("execute", | |
310 [ | |
311 Opts.append, | |
312 Opts.branch, | |
313 Opts.pylib, | |
314 Opts.parallel_mode, | |
315 Opts.module, | |
316 Opts.timid, | |
317 Opts.source, | |
318 Opts.omit, | |
319 Opts.include, | |
320 ] + GLOBAL_ARGS, | |
321 defaults = {'erase_first': True}, | |
322 cmd = "run", | |
323 usage = "[options] <pyfile> [program options]", | |
324 description = "Run a Python program, measuring code execution." | |
325 ), | |
326 | |
327 'xml': CmdOptionParser("xml", | |
328 [ | |
329 Opts.fail_under, | |
330 Opts.ignore_errors, | |
331 Opts.omit, | |
332 Opts.include, | |
333 Opts.output_xml, | |
334 ] + GLOBAL_ARGS, | |
335 cmd = "xml", | |
336 usage = "[options] [modules]", | |
337 description = "Generate an XML report of coverage results." | |
338 ), | |
339 } | |
340 | |
341 | |
342 OK, ERR, FAIL_UNDER = 0, 1, 2 | |
343 | |
344 | |
345 class CoverageScript(object): | |
346 """The command-line interface to Coverage.""" | |
347 | |
348 def __init__(self, _covpkg=None, _run_python_file=None, | |
349 _run_python_module=None, _help_fn=None): | |
350 # _covpkg is for dependency injection, so we can test this code. | |
351 if _covpkg: | |
352 self.covpkg = _covpkg | |
353 else: | |
354 import coverage | |
355 self.covpkg = coverage | |
356 | |
357 # For dependency injection: | |
358 self.run_python_file = _run_python_file or run_python_file | |
359 self.run_python_module = _run_python_module or run_python_module | |
360 self.help_fn = _help_fn or self.help | |
361 self.classic = False | |
362 | |
363 self.coverage = None | |
364 | |
365 def command_line(self, argv): | |
366 """The bulk of the command line interface to Coverage. | |
367 | |
368 `argv` is the argument list to process. | |
369 | |
370 Returns 0 if all is well, 1 if something went wrong. | |
371 | |
372 """ | |
373 # Collect the command-line options. | |
374 if not argv: | |
375 self.help_fn(topic='minimum_help') | |
376 return OK | |
377 | |
378 # The command syntax we parse depends on the first argument. Classic | |
379 # syntax always starts with an option. | |
380 self.classic = argv[0].startswith('-') | |
381 if self.classic: | |
382 parser = ClassicOptionParser() | |
383 else: | |
384 parser = CMDS.get(argv[0]) | |
385 if not parser: | |
386 self.help_fn("Unknown command: '%s'" % argv[0]) | |
387 return ERR | |
388 argv = argv[1:] | |
389 | |
390 parser.help_fn = self.help_fn | |
391 ok, options, args = parser.parse_args(argv) | |
392 if not ok: | |
393 return ERR | |
394 | |
395 # Handle help and version. | |
396 if self.do_help(options, args, parser): | |
397 return OK | |
398 | |
399 # Check for conflicts and problems in the options. | |
400 if not self.args_ok(options, args): | |
401 return ERR | |
402 | |
403 # Listify the list options. | |
404 source = unshell_list(options.source) | |
405 omit = unshell_list(options.omit) | |
406 include = unshell_list(options.include) | |
407 | |
408 # Do something. | |
409 self.coverage = self.covpkg.coverage( | |
410 data_suffix = options.parallel_mode, | |
411 cover_pylib = options.pylib, | |
412 timid = options.timid, | |
413 branch = options.branch, | |
414 config_file = options.rcfile, | |
415 source = source, | |
416 omit = omit, | |
417 include = include, | |
418 ) | |
419 | |
420 if 'debug' in options.actions: | |
421 return self.do_debug(args) | |
422 | |
423 if 'erase' in options.actions or options.erase_first: | |
424 self.coverage.erase() | |
425 else: | |
426 self.coverage.load() | |
427 | |
428 if 'execute' in options.actions: | |
429 self.do_execute(options, args) | |
430 | |
431 if 'combine' in options.actions: | |
432 self.coverage.combine() | |
433 self.coverage.save() | |
434 | |
435 # Remaining actions are reporting, with some common options. | |
436 report_args = dict( | |
437 morfs = args, | |
438 ignore_errors = options.ignore_errors, | |
439 omit = omit, | |
440 include = include, | |
441 ) | |
442 | |
443 if 'report' in options.actions: | |
444 total = self.coverage.report( | |
445 show_missing=options.show_missing, **report_args) | |
446 if 'annotate' in options.actions: | |
447 self.coverage.annotate( | |
448 directory=options.directory, **report_args) | |
449 if 'html' in options.actions: | |
450 total = self.coverage.html_report( | |
451 directory=options.directory, title=options.title, | |
452 **report_args) | |
453 if 'xml' in options.actions: | |
454 outfile = options.outfile | |
455 total = self.coverage.xml_report(outfile=outfile, **report_args) | |
456 | |
457 if options.fail_under is not None: | |
458 if total >= options.fail_under: | |
459 return OK | |
460 else: | |
461 return FAIL_UNDER | |
462 else: | |
463 return OK | |
464 | |
465 def help(self, error=None, topic=None, parser=None): | |
466 """Display an error message, or the named topic.""" | |
467 assert error or topic or parser | |
468 if error: | |
469 print(error) | |
470 print("Use 'coverage help' for help.") | |
471 elif parser: | |
472 print(parser.format_help().strip()) | |
473 else: | |
474 help_msg = HELP_TOPICS.get(topic, '').strip() | |
475 if help_msg: | |
476 print(help_msg % self.covpkg.__dict__) | |
477 else: | |
478 print("Don't know topic %r" % topic) | |
479 | |
480 def do_help(self, options, args, parser): | |
481 """Deal with help requests. | |
482 | |
483 Return True if it handled the request, False if not. | |
484 | |
485 """ | |
486 # Handle help. | |
487 if options.help: | |
488 if self.classic: | |
489 self.help_fn(topic='help') | |
490 else: | |
491 self.help_fn(parser=parser) | |
492 return True | |
493 | |
494 if "help" in options.actions: | |
495 if args: | |
496 for a in args: | |
497 parser = CMDS.get(a) | |
498 if parser: | |
499 self.help_fn(parser=parser) | |
500 else: | |
501 self.help_fn(topic=a) | |
502 else: | |
503 self.help_fn(topic='help') | |
504 return True | |
505 | |
506 # Handle version. | |
507 if options.version: | |
508 self.help_fn(topic='version') | |
509 return True | |
510 | |
511 return False | |
512 | |
513 def args_ok(self, options, args): | |
514 """Check for conflicts and problems in the options. | |
515 | |
516 Returns True if everything is ok, or False if not. | |
517 | |
518 """ | |
519 for i in ['erase', 'execute']: | |
520 for j in ['annotate', 'html', 'report', 'combine']: | |
521 if (i in options.actions) and (j in options.actions): | |
522 self.help_fn("You can't specify the '%s' and '%s' " | |
523 "options at the same time." % (i, j)) | |
524 return False | |
525 | |
526 if not options.actions: | |
527 self.help_fn( | |
528 "You must specify at least one of -e, -x, -c, -r, -a, or -b." | |
529 ) | |
530 return False | |
531 args_allowed = ( | |
532 'execute' in options.actions or | |
533 'annotate' in options.actions or | |
534 'html' in options.actions or | |
535 'debug' in options.actions or | |
536 'report' in options.actions or | |
537 'xml' in options.actions | |
538 ) | |
539 if not args_allowed and args: | |
540 self.help_fn("Unexpected arguments: %s" % " ".join(args)) | |
541 return False | |
542 | |
543 if 'execute' in options.actions and not args: | |
544 self.help_fn("Nothing to do.") | |
545 return False | |
546 | |
547 return True | |
548 | |
549 def do_execute(self, options, args): | |
550 """Implementation of 'coverage run'.""" | |
551 | |
552 # Run the script. | |
553 self.coverage.start() | |
554 code_ran = True | |
555 try: | |
556 try: | |
557 if options.module: | |
558 self.run_python_module(args[0], args) | |
559 else: | |
560 self.run_python_file(args[0], args) | |
561 except NoSource: | |
562 code_ran = False | |
563 raise | |
564 finally: | |
565 self.coverage.stop() | |
566 if code_ran: | |
567 self.coverage.save() | |
568 | |
569 def do_debug(self, args): | |
570 """Implementation of 'coverage debug'.""" | |
571 | |
572 if not args: | |
573 self.help_fn("What information would you like: data, sys?") | |
574 return ERR | |
575 for info in args: | |
576 if info == 'sys': | |
577 print("-- sys ----------------------------------------") | |
578 for label, info in self.coverage.sysinfo(): | |
579 if info == []: | |
580 info = "-none-" | |
581 if isinstance(info, list): | |
582 prefix = "%15s:" % label | |
583 for e in info: | |
584 print("%16s %s" % (prefix, e)) | |
585 prefix = "" | |
586 else: | |
587 print("%15s: %s" % (label, info)) | |
588 elif info == 'data': | |
589 print("-- data ---------------------------------------") | |
590 self.coverage.load() | |
591 print("path: %s" % self.coverage.data.filename) | |
592 print("has_arcs: %r" % self.coverage.data.has_arcs()) | |
593 summary = self.coverage.data.summary(fullpath=True) | |
594 if summary: | |
595 filenames = sorted(summary.keys()) | |
596 print("\n%d files:" % len(filenames)) | |
597 for f in filenames: | |
598 print("%s: %d lines" % (f, summary[f])) | |
599 else: | |
600 print("No data collected") | |
601 else: | |
602 self.help_fn("Don't know what you mean by %r" % info) | |
603 return ERR | |
604 return OK | |
605 | |
606 | |
607 def unshell_list(s): | |
608 """Turn a command-line argument into a list.""" | |
609 if not s: | |
610 return None | |
611 if sys.platform == 'win32': | |
612 # When running coverage as coverage.exe, some of the behavior | |
613 # of the shell is emulated: wildcards are expanded into a list of | |
614 # filenames. So you have to single-quote patterns on the command | |
615 # line, but (not) helpfully, the single quotes are included in the | |
616 # argument, so we have to strip them off here. | |
617 s = s.strip("'") | |
618 return s.split(',') | |
619 | |
620 | |
621 HELP_TOPICS = { | |
622 # ------------------------- | |
623 'classic': | |
624 r"""Coverage.py version %(__version__)s | |
625 Measure, collect, and report on code coverage in Python programs. | |
626 | |
627 Usage: | |
628 | |
629 coverage -x [-p] [-L] [--timid] MODULE.py [ARG1 ARG2 ...] | |
630 Execute the module, passing the given command-line arguments, collecting | |
631 coverage data. With the -p option, include the machine name and process | |
632 id in the .coverage file name. With -L, measure coverage even inside the | |
633 Python installed library, which isn't done by default. With --timid, use a | |
634 simpler but slower trace method. | |
635 | |
636 coverage -e | |
637 Erase collected coverage data. | |
638 | |
639 coverage -c | |
640 Combine data from multiple coverage files (as created by -p option above) | |
641 and store it into a single file representing the union of the coverage. | |
642 | |
643 coverage -r [-m] [-i] [-o DIR,...] [FILE1 FILE2 ...] | |
644 Report on the statement coverage for the given files. With the -m | |
645 option, show line numbers of the statements that weren't executed. | |
646 | |
647 coverage -b -d DIR [-i] [-o DIR,...] [FILE1 FILE2 ...] | |
648 Create an HTML report of the coverage of the given files. Each file gets | |
649 its own page, with the file listing decorated to show executed, excluded, | |
650 and missed lines. | |
651 | |
652 coverage -a [-d DIR] [-i] [-o DIR,...] [FILE1 FILE2 ...] | |
653 Make annotated copies of the given files, marking statements that | |
654 are executed with > and statements that are missed with !. | |
655 | |
656 -d DIR | |
657 Write output files for -b or -a to this directory. | |
658 | |
659 -i Ignore errors while reporting or annotating. | |
660 | |
661 -o DIR,... | |
662 Omit reporting or annotating files when their filename path starts with | |
663 a directory listed in the omit list. | |
664 e.g. coverage -i -r -o c:\python25,lib\enthought\traits | |
665 | |
666 Coverage data is saved in the file .coverage by default. Set the | |
667 COVERAGE_FILE environment variable to save it somewhere else. | |
668 """, | |
669 # ------------------------- | |
670 'help': """\ | |
671 Coverage.py, version %(__version__)s | |
672 Measure, collect, and report on code coverage in Python programs. | |
673 | |
674 usage: coverage <command> [options] [args] | |
675 | |
676 Commands: | |
677 annotate Annotate source files with execution information. | |
678 combine Combine a number of data files. | |
679 erase Erase previously collected coverage data. | |
680 help Get help on using coverage.py. | |
681 html Create an HTML report. | |
682 report Report coverage stats on modules. | |
683 run Run a Python program and measure code execution. | |
684 xml Create an XML report of coverage results. | |
685 | |
686 Use "coverage help <command>" for detailed help on any command. | |
687 Use "coverage help classic" for help on older command syntax. | |
688 For more information, see %(__url__)s | |
689 """, | |
690 # ------------------------- | |
691 'minimum_help': """\ | |
692 Code coverage for Python. Use 'coverage help' for help. | |
693 """, | |
694 # ------------------------- | |
695 'version': """\ | |
696 Coverage.py, version %(__version__)s. %(__url__)s | |
697 """, | |
698 } | |
699 | |
700 | |
701 def main(argv=None): | |
702 """The main entry point to Coverage. | |
703 | |
704 This is installed as the script entry point. | |
705 | |
706 """ | |
707 if argv is None: | |
708 argv = sys.argv[1:] | |
709 try: | |
710 status = CoverageScript().command_line(argv) | |
711 except ExceptionDuringRun: | |
712 # An exception was caught while running the product code. The | |
713 # sys.exc_info() return tuple is packed into an ExceptionDuringRun | |
714 # exception. | |
715 _, err, _ = sys.exc_info() | |
716 traceback.print_exception(*err.args) | |
717 status = ERR | |
718 except CoverageException: | |
719 # A controlled error inside coverage.py: print the message to the user. | |
720 _, err, _ = sys.exc_info() | |
721 print(err) | |
722 status = ERR | |
723 except SystemExit: | |
724 # The user called `sys.exit()`. Exit with their argument, if any. | |
725 _, err, _ = sys.exc_info() | |
726 if err.args: | |
727 status = err.args[0] | |
728 else: | |
729 status = None | |
730 return status | |
OLD | NEW |