Completed
Push — master ( 5dce03...efda8a )
by Ionel Cristian
01:05
created

src.pytest_benchmark.TerminalReporter   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 29
Duplicated Lines 0 %
Metric Value
dl 0
loc 29
rs 10
wmc 9

8 Methods

Rating   Name   Duplication   Size   Complexity  
A write_line() 0 5 2
A write() 0 2 1
A write_sep() 0 3 1
A line() 0 2 1
A section() 0 2 1
A rewrite() 0 3 1
A __init__() 0 2 1
A ensure_newline() 0 2 1
1
import argparse
2
3
import py
4
5
from . import plugin
6
from .logger import Logger
7
from .plugin import add_display_options
8
from .plugin import add_global_options
9
from .storage import Storage
10
from .table import ResultsTable
11
from .utils import first_or_value
12
13
14
class HelpAction(argparse.Action):
15
    def __call__(self, parser, namespace, values, option_string=None):
16
        namespace.command = values
17
        namespace.help = True
18
19
20
class CommandArgumentParser(argparse.ArgumentParser):
21
    commands = None
22
    commands_dispatch = None
23
24
    def __init__(self, *args, **kwargs):
25
        kwargs['add_help'] = False
26
27
        super(CommandArgumentParser, self).__init__(*args,
28
                                                    formatter_class=argparse.RawDescriptionHelpFormatter,
29
                                                    **kwargs)
30
        self.add_argument(
31
            '-h', '--help',
32
            metavar='COMMAND',
33
            nargs='?', action=HelpAction, help='Display help and exit.'
34
        )
35
        self.add_command(
36
            'help',
37
            description='Display help and exit.'
38
        ).add_argument(
39
            'command',
40
            nargs='?', action=HelpAction
41
        )
42
43
    def add_command(self, name, **opts):
44
        if self.commands is None:
45
            self.commands = self.add_subparsers(
46
                title='commands', dest='command', parser_class=argparse.ArgumentParser
47
            )
48
            self.commands_dispatch = {}
49
        if 'description' in opts and 'help' not in opts:
50
            opts['help'] = opts['description']
51
52
        command = self.commands.add_parser(
53
            name, formatter_class=argparse.RawDescriptionHelpFormatter, **opts
54
        )
55
        self.commands_dispatch[name] = command
56
        return command
57
58
    def parse_args(self):
59
        args = super(CommandArgumentParser, self).parse_args()
60
        if args.help:
61
            if args.command:
62
                return super(CommandArgumentParser, self).parse_args([args.command, '--help'])
63
            else:
64
                self.print_help()
65
                self.exit()
66
        elif not args.command:
67
            self.error('the following arguments are required: COMMAND (choose from %s)' % ', '.join(
68
                map(repr, self.commands.choices)))
69
        return args
70
71
72
def strip_prefix(callback, force_argument=False):
73
    def add_argument(dest, **kwargs):
74
        if not dest.startswith('--benchmark-'):
75
            raise RuntimeError("Bad argument %s with options %s" % (dest, kwargs))
76
        callback(dest[12:] if force_argument else '--' + dest[12:], **kwargs)
77
78
    return add_argument
79
80
81
def main():
82
    parser = CommandArgumentParser('py.test-benchmark', description="pytest_benchmark's management commands.")
83
    add_global_options(strip_prefix(parser.add_argument))
84
85
    parser.add_command(
86
        'list',
87
        description='List saved runs.',
88
    )
89
90
    display_command = parser.add_command(
91
        'compare',
92
        description='Compare saved runs.',
93
        epilog='''examples:
94
95
    pytest-benchmark compare 'Linux-CPython-3.5-64bit/*'
96
97
        Loads all benchmarks ran with that interpreter. Note the special quoting that disables your shell's glob
98
        expansion.
99
100
    pytest-benchmark compare 0001
101
102
        Loads first run from all the interpreters.
103
104
    pytest-benchmark compare /foo/bar/0001_abc.json /lorem/ipsum/0001_sir_dolor.json
105
106
        Loads runs from exactly those files.''')
107
    add_display_options(strip_prefix(display_command.add_argument))
108
    display_command.add_argument(
109
        'glob_or_file',
110
        nargs='*', help='Glob or exact path for json files. If not specified all runs are loaded.'
111
    )
112
    args = parser.parse_args()
113
    logger = Logger(args.verbose)
114
    storage = Storage(args.storage, logger)
115
    print(args)
116
    if args.command == 'list':
117
        for file in storage.query():
118
            print(file)
119
    elif args.command == 'compare':
120
        results_table = ResultsTable(args.columns, args.sort, first_or_value(args.histogram, False), logger)
121
        groups = plugin.pytest_benchmark_group_stats(
122
            benchmarks=storage.load_benchmarks(*args.glob_or_file),
123
            group_by=args.group_by,
124
            config=None,
125
        )
126
        results_table.display(TerminalReporter(), groups)
127
128
129
class TerminalReporter(object):
130
    def __init__(self):
131
        self._tw = py.io.TerminalWriter()
132
133
    def ensure_newline(self):
134
        self._tw.line()
135
136
    def write(self, content, **markup):
137
        self._tw.write(content, **markup)
138
139
    def write_line(self, line, **markup):
140
        if not py.builtin._istext(line):
141
            line = py.builtin.text(line, errors="replace")
142
        self.ensure_newline()
143
        self._tw.line(line, **markup)
144
145
    def rewrite(self, line, **markup):
146
        line = str(line)
147
        self._tw.write("\r" + line, **markup)
148
149
    def write_sep(self, sep, title=None, **markup):
150
        self.ensure_newline()
151
        self._tw.sep(sep, title, **markup)
152
153
    def section(self, title, sep="=", **kw):
154
        self._tw.sep(sep, title, **kw)
155
156
    def line(self, msg, **kw):
157
        self._tw.line(msg, **kw)
158