Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: sky/tools/webkitpy/thirdparty/coverage/cmdline.py

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

Powered by Google App Engine
This is Rietveld 408576698