10
10
11
11
from monotonic import monotonic
12
12
13
+ from detect_secrets .core .color import AnsiColor
14
+ from detect_secrets .core .color import colorize
13
15
from detect_secrets .core .usage import PluginOptions
16
+ from detect_secrets .util import get_root_directory
14
17
15
18
16
19
def main ():
@@ -92,7 +95,7 @@ def get_arguments():
92
95
parser .add_argument (
93
96
'--harakiri' ,
94
97
default = 5 ,
95
- type = float ,
98
+ type = assert_positive ( float ) ,
96
99
help = (
97
100
'Specifies an upper bound for the number of seconds to wait '
98
101
'per execution.'
@@ -102,40 +105,59 @@ def get_arguments():
102
105
'-n' ,
103
106
'--num-iterations' ,
104
107
default = 1 ,
105
- type = assert_positive_integer ,
108
+ type = assert_positive ( int ) ,
106
109
help = (
107
110
'Specifies the number of times to run the test. '
108
111
'Results will be averaged over this value.'
109
112
),
110
113
)
114
+ parser .add_argument (
115
+ '--baseline' ,
116
+ type = assert_valid_file ,
117
+ help = (
118
+ 'If provided, will compare performance with provided baseline. '
119
+ 'Assumes pretty output (otherwise, you can do the comparison '
120
+ 'yourself).'
121
+ ),
122
+ )
111
123
112
124
args = parser .parse_args ()
113
125
if not args .filenames :
114
- args .filenames = [
115
- os .path .realpath (
116
- os .path .join (
117
- os .path .dirname (__file__ ),
118
- '../' ,
119
- ),
120
- ),
121
- ]
126
+ if args .baseline :
127
+ args .filenames = args .baseline ['filenames' ]
128
+ else :
129
+ args .filenames = [get_root_directory ()]
122
130
123
131
if not args .plugin :
124
132
args .plugin = plugins
125
133
126
134
return args
127
135
128
136
129
- def assert_positive_integer (string ):
130
- value = int (string )
131
- if value <= 0 :
137
+ def assert_positive (type ):
138
+ def wrapped (string ):
139
+ value = type (string )
140
+ if value <= 0 :
141
+ raise argparse .ArgumentTypeError (
142
+ '{} must be a positive {}.' .format (
143
+ string ,
144
+ type .__name__ ,
145
+ ),
146
+ )
147
+
148
+ return value
149
+
150
+ return wrapped
151
+
152
+
153
+ def assert_valid_file (string ):
154
+ if not os .path .isfile (string ):
132
155
raise argparse .ArgumentTypeError (
133
- '{} must be a positive integer.' .format (
134
- string ,
135
- ),
156
+ '{} must be a valid file.' .format (string ),
136
157
)
137
158
138
- return value
159
+ with open (string ) as f :
160
+ return json .load (f )
139
161
140
162
141
163
def time_execution (filenames , timeout , num_iterations = 1 , flags = None ):
@@ -166,39 +188,125 @@ def time_execution(filenames, timeout, num_iterations=1, flags=None):
166
188
if result == timeout :
167
189
return None
168
190
169
- return statistics .mean (scores )
191
+ return round ( statistics .mean (scores ), 5 )
170
192
171
193
172
194
def print_output (timings , args ):
173
195
"""
174
196
:type timings: dict
175
197
:type args: Namespace
176
198
"""
177
- if not args .pretty :
178
- print (json .dumps (timings ))
199
+ if not args .pretty and not args .baseline :
200
+ print (
201
+ json .dumps ({
202
+ 'filenames' : args .filenames ,
203
+ 'timings' : timings ,
204
+ }),
205
+ )
179
206
return
180
207
181
208
# Print header
182
- print ('-' * 42 )
183
- print ('{:<20s}{:>20s}' .format ('plugin' , 'time' ))
184
- print ('-' * 42 )
209
+ baseline = args .baseline ['timings' ] if args .baseline else {}
210
+ if not baseline :
211
+ print ('-' * 45 )
212
+ print ('{:<25s}{:>15s}' .format ('plugin' , 'time' ))
213
+ print ('-' * 45 )
214
+ else :
215
+ print ('-' * 60 )
216
+ print ('{:<25s}{:>11s}{:>22s}' .format ('plugin' , 'time' , 'change' ))
217
+ print ('-' * 60 )
185
218
219
+ # Print content
186
220
if 'all-plugins' in timings :
187
- print_line ('all-plugins' , timings ['all-plugins' ])
221
+ print_line (
222
+ 'All Plugins' ,
223
+ time = timings ['all-plugins' ],
224
+ baseline = _get_baseline_value (baseline , 'all-plugins' ),
225
+ timeout = args .harakiri ,
226
+ )
188
227
del timings ['all-plugins' ]
189
228
190
229
for key in sorted (timings ):
191
- print_line (key , timings [key ])
192
- print ('-' * 42 )
230
+ print_line (
231
+ key ,
232
+ time = timings [key ],
233
+ baseline = _get_baseline_value (baseline , key ),
234
+ timeout = args .harakiri ,
235
+ )
193
236
237
+ # Print footer line
238
+ if not args .baseline :
239
+ print ('-' * 45 )
240
+ else :
241
+ print ('-' * 60 )
242
+
243
+
244
+ def _get_baseline_value (baseline , key ):
245
+ """
246
+ We need to distinguish between no baseline mode (which should return
247
+ None as a value), baseline mode with exceeded timeout (which is stored
248
+ as None, but should return 0).
249
+ """
250
+ if key in baseline :
251
+ return 0 if baseline [key ] is None else baseline [key ]
252
+
253
+
254
+ def print_line (name , time , baseline , timeout ):
255
+ """
256
+ :type name: str
257
+
258
+ :type time: float
259
+ :param time: seconds it took to execute
260
+
261
+ :type baseline: float
262
+ :param baseline: expected seconds to execute
194
263
195
- def print_line (name , time ):
264
+ :type timeout: float
265
+ :param timeout: used to calculate difference when either current
266
+ execution or baseline execution exceeds timeout.
267
+ """
196
268
if not time :
197
- time = 'Timeout exceeded!'
269
+ time_string = 'Timeout exceeded!'
198
270
else :
199
- time = '{}s' .format (str (time ))
271
+ time_string = '{}s' .format (str (time ))
272
+
273
+ if baseline is not None :
274
+ if time and baseline :
275
+ difference = round (baseline - time , 2 )
276
+ elif time :
277
+ # This handles the case when the baseline execution exceeds timeout
278
+ difference = round (timeout - time , 2 )
279
+ elif baseline :
280
+ # This handles the case when this current execution exceeds timeout
281
+ difference = round (timeout - baseline , 2 )
282
+ else :
283
+ # They both failed.
284
+ difference = 0
285
+
286
+ if difference > 0 :
287
+ difference_string = colorize (
288
+ '▲ {}' .format (difference ),
289
+ AnsiColor .LIGHT_GREEN ,
290
+ )
291
+ difference_string = '{:>22s}' .format (difference_string )
292
+ elif difference < 0 :
293
+ difference_string = colorize (
294
+ '▼ {}' .format (difference ),
295
+ AnsiColor .RED ,
296
+ )
297
+ difference_string = '{:>22s}' .format (difference_string )
298
+ else :
299
+ difference_string = '{:>10s}' .format ('-' )
200
300
201
- print ('{:<20s}{:>20s}' .format (name , time ))
301
+ print (
302
+ '{:<25s}{:^20s}{}' .format (
303
+ name ,
304
+ time_string ,
305
+ difference_string ,
306
+ ),
307
+ )
308
+ else :
309
+ print ('{:<25s}{:>20s}' .format (name , time_string ))
202
310
203
311
204
312
if __name__ == '__main__' :
0 commit comments