~inkscape.dev/inkscape-devlibs64/trunk

« back to all changes in this revision

Viewing changes to python/Lib/site-packages/coverage/cmdline.py

  • Committer: Eduard Braun
  • Date: 2016-10-22 16:51:19 UTC
  • Revision ID: eduard.braun2@gmx.de-20161022165119-9eosgy6lp8j1kzli
Update Python to version 2.7.12

Included modules:
  coverage 4.2
  lxml 3.6.4
  numpy 1.11.2
  scour 0.35
  six 1.10.0

Show diffs side-by-side

added added

removed removed

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