perftester 0.4.0__py3-none-any.whl → 0.5.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
perftester/perftester.py CHANGED
@@ -285,7 +285,7 @@ class Config:
285
285
  ]
286
286
  self.memory_benchmark = min(max(r) for r in memory_results)
287
287
 
288
- def set_defaults(self, which, number=None, repeat=None):
288
+ def set_defaults(self, which, number=None, repeat=None, Number=None, Repeat=None):
289
289
  """Change the default settings.
290
290
 
291
291
  Beware! This does not change particular settings for a particular test,
@@ -302,6 +302,11 @@ class Config:
302
302
  repeat (int, optional): passed to timeit.repeat as repeat.
303
303
  Defaults to None.
304
304
  """
305
+ if number is None and Number is not None:
306
+ number = Number
307
+ if repeat is None and Repeat is not None:
308
+ repeat = Repeat
309
+
305
310
  self._check_args(lambda: 0, which, number, repeat)
306
311
 
307
312
  if number is not None:
@@ -309,7 +314,7 @@ class Config:
309
314
  if repeat is not None:
310
315
  self.defaults[which]["repeat"] = repeat
311
316
 
312
- def set(self, func, which, number=None, repeat=None):
317
+ def set(self, func, which, number=None, repeat=None, Number=None, Repeat=None):
313
318
  """Set a particular argument.
314
319
 
315
320
  Args:
@@ -322,6 +327,11 @@ class Config:
322
327
  as repeat; for memory tests, the number of runs of the test.
323
328
  Defaults to None.
324
329
  """
330
+ if number is None and Number is not None:
331
+ number = Number
332
+ if repeat is None and Repeat is not None:
333
+ repeat = Repeat
334
+
325
335
  self._check_args(func, which, number, repeat)
326
336
 
327
337
  if func not in self.settings.keys():
@@ -371,6 +381,12 @@ class Config:
371
381
  "For memory tests, you can only set repeat, not number.",
372
382
  )
373
383
 
384
+ if number is not None:
385
+ if int(number) == number:
386
+ number = int(number)
387
+ if repeat is not None:
388
+ if int(repeat) == repeat:
389
+ repeat = int(repeat)
374
390
  check_instance(
375
391
  number,
376
392
  (int, None),
@@ -380,7 +396,6 @@ class Config:
380
396
  f"{type(number).__name__}"
381
397
  ),
382
398
  )
383
-
384
399
  check_instance(
385
400
  repeat,
386
401
  (int, None),
@@ -805,7 +820,7 @@ def pp(*args):
805
820
  0.1222
806
821
  >>> pp(dict(a=.12121212, b=23.234234234), ["system failure", 345345.345])
807
822
  {'a': 0.1212, 'b': 23.23}
808
- ['system failure', 345300]
823
+ ['system failure', 345300.0]
809
824
  """
810
825
  for arg in args:
811
826
  pprint(
perftester/tmp.py ADDED
@@ -0,0 +1,69 @@
1
+ from pprint import pprint
2
+
3
+
4
+ class Container:
5
+ def __init__(self, values):
6
+ self.values = values
7
+
8
+ def __gt__(self, other):
9
+ return Container(type(self.values)(other(i) for i in self.values))
10
+
11
+
12
+ class Show:
13
+ def __call__(self, obj=None, func=None, *args, **kwargs):
14
+ self.obj = obj
15
+ if not func:
16
+ pprint(obj.result)
17
+ else:
18
+ pprint(func(obj.result, *args, **kwargs))
19
+ return obj
20
+
21
+ def __gt__(self, other):
22
+ return other(self.obj.result)
23
+
24
+
25
+
26
+ class Value:
27
+ def __init__(self, value):
28
+ self.result = value
29
+
30
+ def __call__(self):
31
+ return self.result
32
+
33
+ def __gt__(self, other):
34
+ return other(self.result)
35
+
36
+
37
+ class Do:
38
+ def __init__(self, func, *args, parallel=False, **kwargs):
39
+ self.func = func
40
+ self.args = args
41
+ self.kwargs = kwargs
42
+ self.result = None
43
+ self.parallel = parallel
44
+
45
+ def __call__(self, *add_args, **add_kwargs):
46
+ these_args = {**self.kwargs, **add_kwargs}
47
+ self.result = self.func(*add_args, *self.args, **these_args)
48
+ return self.result
49
+
50
+ def __gt__(self, other):
51
+ if isinstance(other, Show):
52
+ return other(self)
53
+ try:
54
+ return other(self.__call__())
55
+ except:
56
+ return other(self.result)
57
+
58
+
59
+
60
+ double = lambda x: 2 * x
61
+ square = lambda x: x ** 2
62
+ add = lambda x, y: x + y
63
+
64
+ Value(10) > Do(double) > Show() > Do(double) > Show()
65
+
66
+ Value(10) > Do(double) > Do(square) > Do(add, 50) > Do(print)
67
+ Value(10) > Do(double) > Do(square) > Show() > Do(add, 50) > Show()
68
+
69
+ Container([1, 2, 3]) > Do(double) > Show()
@@ -0,0 +1,34 @@
1
+ class Value:
2
+ def __init__(self, value):
3
+ self.result = value
4
+
5
+ def __call__(self):
6
+ return self.result
7
+
8
+ def __gt__(self, other):
9
+ return other(self.result)
10
+
11
+
12
+ class Do:
13
+ def __init__(self, func=None, *args, **kwargs):
14
+ self.func = func
15
+ self.args = args
16
+ self.kwargs = kwargs
17
+
18
+ def __call__(self, *add_args, **add_kwargs):
19
+ these_args = {**self.kwargs, **add_kwargs}
20
+ self.result = self.func(*add_args, *self.args, **these_args)
21
+ return self.result
22
+
23
+ def __gt__(self, other):
24
+ try:
25
+ return other(self.__call__())
26
+ except:
27
+ return other(self.result)
28
+
29
+
30
+ double = lambda x: 2 * x
31
+ square = lambda x: x ** 2
32
+ add = lambda x, y: x + y
33
+
34
+ Value(10) > Do(double) > Do(square) > Do(add, 50) > Do(print)
@@ -0,0 +1,476 @@
1
+ import ast
2
+ import copy
3
+ import inspect
4
+ import json
5
+ import pathlib
6
+ import pickle
7
+ import rounder
8
+
9
+ import easycheck
10
+
11
+ from collections import namedtuple
12
+ from copy import deepcopy
13
+ from functools import wraps
14
+ from memory_profiler import memory_usage
15
+ from time import perf_counter
16
+ from typing import Callable, Iterable, Union
17
+
18
+
19
+ # Globals, used as dict fields in calls
20
+
21
+ CALLEDFROM = "called from"
22
+ CALLMODE = "calls"
23
+ INSTANCEMODE = "instances"
24
+ CALLS = "no of calls"
25
+ INSTANCES = "no of instances"
26
+ MEM = "memory peak"
27
+ TIME = "execution time"
28
+ TIME_ALL = "time spent inside (all)"
29
+ TIME_MEAN = "time spent inside (mean)"
30
+
31
+
32
+ # Exceptions
33
+
34
+ class IncorrectDictRepresentationError(Exception):
35
+ pass
36
+
37
+
38
+ # Performance decorators
39
+
40
+ def time_performance(func: Callable) -> Callable:
41
+ """Time performance decorator.
42
+
43
+ It is used to analyze the performance of the decorated function in terms
44
+ of execution time.
45
+ """
46
+ @wraps(func)
47
+ def inner(*args, **kwargs):
48
+ _type, function = _get_type_and_name(func)
49
+ if _type == "class method":
50
+ aargs = list(args)
51
+ aargs[0] = "self"
52
+ aargs = tuple(aargs)
53
+ arguments = _stringify_args_kwargs(aargs, kwargs)
54
+ else:
55
+ arguments = _stringify_args_kwargs(args, kwargs)
56
+
57
+ global calls
58
+
59
+ call_type = INSTANCEMODE if _type == "class" else CALLMODE
60
+ call_count = INSTANCES if _type == "class" else CALLS
61
+
62
+ if not calls.registered[_type].get(function, False):
63
+ calls.registered[_type][function] = {}
64
+ calls.registered[_type][function][call_type] = {}
65
+
66
+ calls.registered[_type][function][call_count] = (
67
+ calls.registered[_type][function].get(call_count, 0) + 1
68
+ )
69
+
70
+ start = perf_counter()
71
+ ret = func(*args, **kwargs)
72
+ end = perf_counter()
73
+
74
+ ID = calls.registered[_type][function][call_count]
75
+ calls.registered[_type][function][call_type][ID] = {
76
+ CALLEDFROM: inspect.stack()[1][3],
77
+ }
78
+ calls.registered[_type][function][call_type][ID][TIME] = (
79
+ rounder.signif_object(end - start, 5)
80
+ )
81
+ calls.registered[_type][function][call_type][ID]["call"] = f"{function}({arguments})"
82
+ calls.registered[_type][function][TIME_ALL] = (
83
+ calls
84
+ .registered[_type][function]
85
+ .get(TIME_ALL, 0)
86
+ + (end - start)
87
+ )
88
+ calls.registered[_type][function][TIME_MEAN] = (
89
+ calls.registered[_type][function][TIME_ALL] /
90
+ (
91
+ calls.registered[_type][function].get(CALLS, None)
92
+ or calls.registered[_type][function][INSTANCES]
93
+ )
94
+ )
95
+ return ret
96
+
97
+ return inner
98
+
99
+
100
+ def memory_performance(func: Callable) -> Callable:
101
+ """Memory performance decorator.
102
+
103
+ It is used to analyze the performance of the decorated function in terms
104
+ of memory usage.
105
+ """
106
+
107
+ def inner(*args, **kwargs):
108
+ _type, function = _get_type_and_name(func)
109
+ if _type == "class method":
110
+ aargs = list(args)
111
+ aargs[0] = "self"
112
+ aargs = tuple(aargs)
113
+ arguments = _stringify_args_kwargs(aargs, kwargs)
114
+ else:
115
+ arguments = _stringify_args_kwargs(args, kwargs)
116
+
117
+ global calls
118
+
119
+ call_type = INSTANCEMODE if _type == "class" else CALLMODE
120
+ call_count = INSTANCES if _type == "class" else CALLS
121
+
122
+ if not calls.registered[_type].get(function, False):
123
+ calls.registered[_type][function] = {}
124
+ calls.registered[_type][function][call_type] = {}
125
+
126
+ calls.registered[_type][function][call_count] = (
127
+ calls.registered[_type][function].get(call_count, 0) + 1
128
+ )
129
+ memory_results, ret = memory_usage((func, args, kwargs), retval=True)
130
+ peak_memory = min(memory_results)
131
+
132
+ ID = calls.registered[_type][function][call_count]
133
+ calls.registered[_type][function][call_type][ID] = {
134
+ CALLEDFROM: inspect.stack()[1][3],
135
+ }
136
+ calls.registered[_type][function][call_type][ID][MEM] = (
137
+ peak_memory
138
+ )
139
+ calls.registered[_type][function][call_type][ID]["call"] = f"{function}({arguments})"
140
+ calls.registered[_type][function][MEM] = max(
141
+ # current max
142
+ calls
143
+ .registered[_type][function]
144
+ .get(MEM, 0),
145
+ # current memory_peak
146
+ peak_memory
147
+ )
148
+ return ret
149
+
150
+ return inner
151
+
152
+
153
+ # Class containing all the registered calls and class instances.
154
+
155
+ class Calls:
156
+ __slots__ = ("registered", "analyze", "save", "read")
157
+
158
+ def __init__(self):
159
+ self.registered: dict = {"class": {}, "class method": {}, "function": {}}
160
+
161
+ def show(self, digits=4, indent=4):
162
+ rounded_calls = rounder.signif_object(self.registered,
163
+ use_copy=True,
164
+ digits=digits)
165
+ print("Classes:\n", json.dumps(rounded_calls["class"], indent=indent))
166
+ print("Class methods:\n", json.dumps(rounded_calls["class method"], indent=indent))
167
+ print("Functions:\n", json.dumps(rounded_calls["function"], indent=indent))
168
+
169
+ def __repr__(self):
170
+ if not self.registered.keys():
171
+ return "No registered calls"
172
+
173
+ instances = self._get_count("class")
174
+ method_calls = self._get_count("class method")
175
+ func_calls = self._get_count("function")
176
+
177
+ return (
178
+ "Registered:\n"
179
+ f" * {instances} class instances\n"
180
+ f" * {method_calls} class methods\n"
181
+ f" * {func_calls} function calls\n"
182
+ )
183
+
184
+ def _get_count(self, which):
185
+ calls = 0
186
+ if self.registered[which]:
187
+ for k in self.registered[which]:
188
+ key = INSTANCES if which == "class" else CALLS
189
+ calls += self.registered[which][k][key]
190
+ return calls
191
+
192
+
193
+ class CallsAnalyzer:
194
+ """Class to analyze Calls.registered.
195
+
196
+ This is a simple analyzer. A more adanced one will be offered
197
+ in a dedicated understand extension.
198
+ """
199
+ def _update_calls(self):
200
+ global calls
201
+ self.calls = calls.registered
202
+
203
+ def summarize(self, digits=4) -> dict:
204
+ self._update_calls()
205
+ summary_calls = copy.deepcopy(self.calls)
206
+ for key in ("class method", "function"):
207
+ if summary_calls[key]:
208
+ for k in summary_calls[key]:
209
+ _ = summary_calls[key][k].pop(CALLMODE)
210
+ if summary_calls["class"]:
211
+ for k in summary_calls["class"]:
212
+ _ = summary_calls["class"][k].pop(INSTANCEMODE)
213
+
214
+ summary_calls = rounder.signif_object(summary_calls, digits=digits)
215
+ return summary_calls
216
+
217
+ summarise = summarize
218
+
219
+
220
+ class CallsSaver:
221
+ def _update(self):
222
+ global calls
223
+ self.calls = calls.registered
224
+
225
+ def to_json(self, path: Union[str, pathlib.Path]):
226
+ self._update()
227
+ json_dict = json.dumps(self.calls)
228
+ with open(path, "w") as json_file:
229
+ json_file.write(json_dict)
230
+
231
+ def to_text(self, path: Union[str, pathlib.Path]):
232
+ self._update()
233
+ with open(path, "w") as text_file:
234
+ text_file.write(str(self.calls))
235
+
236
+ def to_pickle(self, path: Union[str, pathlib.Path]):
237
+ self._update()
238
+ with open(path, "wb") as pickle_file:
239
+ pickle.dump(self.calls, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
240
+
241
+
242
+ class IncorrectCallsInstanceError(Exception): ...
243
+
244
+
245
+ CallsInstance = namedtuple("CallsInstance", "type registered")
246
+
247
+
248
+ class CallsReader:
249
+ __slots__ = tuple()
250
+
251
+ def _check_calls_object(self, obj, path):
252
+ # check if obj is a valid Calls object
253
+ easycheck.check_type(
254
+ obj,
255
+ expected_type=dict,
256
+ handle_with=IncorrectDictRepresentationError,
257
+ message=f"Object parsed from {path} is not a dict but"
258
+ f" a {type(obj).__name__}"
259
+ )
260
+ # check if the required keys are there
261
+ return self._get_type_of_calls(obj)
262
+
263
+ @staticmethod
264
+ def _get_type_of_calls(obj):
265
+ # check if this is a time or memory calls dict
266
+ # if time: return "time"
267
+ # if memory: return "memory"
268
+ # else: raise IncorrectCallsInstanceError
269
+ for _type in ["class", "class method", "function"]:
270
+ if obj[_type].keys():
271
+ first = list(obj[_type].keys())[0]
272
+ if TIME_ALL in obj[_type][first].keys():
273
+ return "time"
274
+ if MEM in obj[_type][first].keys():
275
+ return "memory"
276
+ raise IncorrectCallsInstanceError(
277
+ "The object is neither Time nor Memory Calls dictionary."
278
+ )
279
+
280
+ def from_json(self, path: Union[str, pathlib.Path]):
281
+ with open(path) as jsonfile:
282
+ registered = json.load(jsonfile)
283
+
284
+ # use int IDs (used as keys) instead of str,
285
+ # which is used by json.load
286
+ for _type in ["class", "class method", "function"]:
287
+ calls = "instances" if _type == "class" else "calls"
288
+ for method in registered[_type]:
289
+ registered[_type][method][calls] = {
290
+ int(instance): value
291
+ for instance, value in registered[_type][method][calls].items()
292
+ }
293
+ return CallsInstance(
294
+ type=self._check_calls_object(registered, path),
295
+ registered=registered
296
+ )
297
+
298
+ def from_text(self, path: Union[str, pathlib.Path]):
299
+ with open(path) as f:
300
+ try:
301
+ obj_from_text = ast.literal_eval(f.read())
302
+ except Exception as e:
303
+ raise IncorrectDictRepresentationError from e
304
+ self._check_calls_object(obj_from_text, path)
305
+ return CallsInstance(
306
+ type=self._check_calls_object(obj_from_text, path),
307
+ registered=obj_from_text
308
+ )
309
+
310
+ def from_pickle(self, path: Union[str, pathlib.Path]):
311
+ with open(path, "rb") as pickle_file:
312
+ pkl = pickle.load(pickle_file)
313
+ return CallsInstance(
314
+ type=self._check_calls_object(pkl, path),
315
+ registered=pkl
316
+ )
317
+
318
+
319
+ # Helpers
320
+
321
+ def _is_class(obj):
322
+ return repr(obj).startswith("<class")
323
+
324
+
325
+ def _is_this_function_a_method(func):
326
+ return "." in func.__qualname__
327
+
328
+
329
+ def _get_type_and_name(obj):
330
+ if _is_class(obj):
331
+ return "class", f"{obj.__module__}.{obj.__name__}"
332
+ if hasattr(obj, "__wrapped__"):
333
+ name = obj.__wrapped__.__name__
334
+ if _is_this_function_a_method(obj):
335
+ name = obj.__qualname__
336
+ return "class method", f"{obj.__module__}.{name}"
337
+ return "function", f"{obj.__module__}.{obj.__name__}"
338
+
339
+
340
+ def type_and_len_of_iterable(it, max_len=1000):
341
+ """Get length of an iterable.
342
+
343
+ The iterable is copied first, and the function works
344
+ on the deep copy. For generators expressions, the function
345
+ returns "generator" as cannot get its length without emptying
346
+ the original generator.
347
+
348
+ If maximum_len is reached, instead of the actual length, the function
349
+ returns "over {max_len}", to make the function cheap.
350
+ """
351
+ # Get length, based on copy
352
+ it_copy = deepcopy(it)
353
+
354
+ c = 0
355
+ max_len_reached = False
356
+ for i in it_copy:
357
+ c += 1
358
+ if i == max_len:
359
+ max_len_reached = True
360
+ break
361
+
362
+ if max_len_reached:
363
+ return f"{type(it).__name__} of over {max_len} items"
364
+ if not c:
365
+ return f"empty {type(it).__name__}"
366
+ return f"{type(it).__name__} of {c} item{'s' if c > 1 else ''}"
367
+
368
+
369
+ def _add_quotes_to_str(obj):
370
+ """Add quotes to strings.
371
+
372
+ The function works recursively, so that it goes deep into the structure
373
+ to reach any string. If obj is "self", the quotes are not added, so that
374
+ in case of class methods self is printed without quotes.
375
+
376
+ Warning: Note that in a dictionary, it will add quotes only to values,
377
+ not to keys.
378
+
379
+ >>> _add_quotes_to_str(222)
380
+ 222
381
+ >>> _add_quotes_to_str("222")
382
+ "'222'"
383
+ >>> _add_quotes_to_str(("222", ))
384
+ 'tuple of 1 item'
385
+ >>> _add_quotes_to_str(("222", 50, "String", 'Also string'))
386
+ 'tuple of 4 items'
387
+ >>> _add_quotes_to_str(("222", 50, ["String", 'Also string']))
388
+ 'tuple of 3 items'
389
+ >>> _add_quotes_to_str((["'whatever'", "'and whatever'"], 20))
390
+ 'tuple of 2 items'
391
+ >>> _add_quotes_to_str({"a": "whatever", "b":"Something"})
392
+ {'a': "'whatever'", 'b': "'Something'"}
393
+ """
394
+ if obj == "self":
395
+ return f"self"
396
+ if not obj:
397
+ return obj
398
+
399
+ if isinstance(obj, dict):
400
+ for k, v in obj.items():
401
+ obj[k] = _add_quotes_to_str(v)
402
+ return obj
403
+ if isinstance(obj, str):
404
+ return f"'{obj}'"
405
+ if isinstance(obj, range):
406
+ n = len(obj)
407
+ return f"range of {n} item{'s' if n > 1 else ''}"
408
+ if isinstance(obj, Iterable):
409
+ # For generator expression, we need to use itertools.tee,
410
+ # but this cannot be done behind the scenes, as the original
411
+ # object is empty and cannot be thus used.
412
+ try:
413
+ return type_and_len_of_iterable(obj)
414
+ except TypeError:
415
+ # cannot pickle the generator type,
416
+ # hence we do not get its length
417
+ return "generator"
418
+
419
+ return obj
420
+
421
+
422
+ def _stringify_args_kwargs(args, kwargs, digits=4):
423
+ """Make a string out of args and kwargs.
424
+
425
+ The final string is formatted in a way that can be used in a print
426
+ of a call to a function. So, for instance, white spaces are added after
427
+ each argument; quotes are added to strings, etc.
428
+
429
+ >>> _stringify_args_kwargs(("something", 20), {})
430
+ "'something', 20"
431
+ >>> _stringify_args_kwargs(tuple(), {"x": 300})
432
+ 'x=300'
433
+ >>> _stringify_args_kwargs(tuple(), {"x": "mixed"})
434
+ "x='mixed'"
435
+ >>> _stringify_args_kwargs(("something", 20), {"x": 300})
436
+ "'something', 20, x=300"
437
+ >>> _stringify_args_kwargs(("something", 20), {"x": "something else"})
438
+ "'something', 20, x='something else'"
439
+ """
440
+ arguments = ""
441
+ if len(args) > 0 and len(kwargs) == 0:
442
+ if len(args) == 1:
443
+ a = _add_quotes_to_str(rounder.signif_object(args[0], digits))
444
+ arguments = f"{a}"
445
+ else:
446
+ to_join = [str(_add_quotes_to_str(rounder.signif_object(a, digits))) for a in args]
447
+ arguments = f"{', '.join(to_join)}"
448
+ elif len(kwargs) > 0 and len(args) == 0:
449
+ a = ", ".join(f"{k}={str(_add_quotes_to_str(rounder.signif_object(v, digits)))}" for k, v in kwargs.items())
450
+ arguments = f'{a}'
451
+ elif len(kwargs) > 0 and len(args) > 0:
452
+ if len(args) == 1:
453
+ arguments = f"{_add_quotes_to_str(rounder.signif_object(args[0], digits))}"
454
+ else:
455
+ to_join = [str(_add_quotes_to_str(rounder.signif_object(a, digits))) for a in args]
456
+ arguments = f"{', '.join(to_join)}"
457
+ if len(kwargs) == 1:
458
+ key = next(iter(kwargs))
459
+ arguments += f", {key}={_add_quotes_to_str(rounder.signif_object(kwargs[key], digits))}"
460
+ else:
461
+ arguments += f', {", ".join(f"{k}={_add_quotes_to_str(rounder.signif_object(v, digits))}" for k, v in kwargs.items())}'
462
+ return arguments
463
+
464
+
465
+ # The final creation of the instance of Calls
466
+
467
+ calls = Calls()
468
+ calls.analyze = CallsAnalyzer()
469
+ calls.save = CallsSaver()
470
+ calls.read = CallsReader()
471
+
472
+
473
+ if __name__ == "__main__":
474
+ import doctest
475
+
476
+ doctest.testmod()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: perftester
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  Summary: Lightweight performance testing in Python
5
5
  Home-page: https://github.com/nyggus/perftester
6
6
  Author: Nyggus
@@ -13,11 +13,11 @@ Classifier: Operating System :: OS Independent
13
13
  Requires-Python: >=3.8
14
14
  Description-Content-Type: text/markdown
15
15
  Requires-Dist: easycheck
16
- Requires-Dist: memory-profiler (==0.60.0)
16
+ Requires-Dist: memory-profiler
17
17
  Requires-Dist: rounder
18
18
  Provides-Extra: dev
19
19
  Requires-Dist: black ; extra == 'dev'
20
- Requires-Dist: wheel (==0.37.1) ; extra == 'dev'
20
+ Requires-Dist: wheel ; extra == 'dev'
21
21
 
22
22
  # `perftester`: Lightweight performance testing of Python functions
23
23
 
@@ -49,7 +49,7 @@ pt.time_benchmark(foo, x=129, n=100)
49
49
  ```
50
50
  and this will print the results of the time benchmark, with raw results similar to those that `timeit.repeat()` returns, but unlike it, `pt.time_benchmark()` returns mean raw time per function run, not overall; in additional, you will see some summaries of the results.
51
51
 
52
- The above call did actually run `timeit.repeat()` function, with the default configuration of `number=100_000` and `repeat=5`. If you want to change any of these, you can use arguments `Number` and `Repeat`, correspondigly:
52
+ The above call did actually run `timeit.repeat()` function, with the default configuration of `Number=100_000` and `Repeat=5`. If you want to change any of these, you can use arguments `Number` and `Repeat`, correspondigly:
53
53
 
54
54
  ```python
55
55
  pt.time_benchmark(foo, x=129, n=100, Number=1000)
@@ -59,7 +59,7 @@ pt.time_benchmark(foo, x=129, n=100, Number=1000, Repeat=2)
59
59
 
60
60
  These calls do not change the default settings so you use the arguments' values on the fly. Later you will learn how to change the default settings and the settings for a particular function.
61
61
 
62
- > Some of you may wonder why the `Number` and `Repeat` arguments violate what we can call the Pythonic style, by using a capital first letter for function arguments. The reason is simple: I wanted to minimize a risk of conflicts that would happen when benchmarking (or testing) a function with any of the arguments `number` or `repeat` (or both). A chance that a Python function will have a `Number` or a `Repeat` argument is rather small. If that happens, however, you can use `functools.partial()` to overcome the problem:
62
+ > Some of you may wonder why the `Number` and `Repeat` arguments violate what we can call the Pythonic style, by using a capital first letter for function arguments. The reason is simple: I wanted to minimize a risk of conflicts that would happen when benchmarking (or testing) a function with any of the arguments `Number` or `Repeat` (or both). A chance that a Python function will have a `Number` or a `Repeat` argument is rather small. If that happens, however, you can use `functools.partial()` to overcome the problem:
63
63
 
64
64
  ```python
65
65
  from functools import partial
@@ -217,7 +217,7 @@ To create a performance test for a function, you likely need to know how it beha
217
217
  ```python
218
218
  >>> import perftester as pt
219
219
  >>> def f(n): return sum(map(lambda i: i**0.5, range(n)))
220
- >>> pt.config.set(f, "time", number=1000)
220
+ >>> pt.config.set(f, "time", Number=1000)
221
221
  >>> b_100_time = pt.time_benchmark(f, n=100)
222
222
  >>> b_100_memory = pt.memory_usage_benchmark(f, n=100)
223
223
  >>> b_1000_time = pt.time_benchmark(f, n=1000)
@@ -334,7 +334,7 @@ The whole configuration is stored in the `pt.config` object, which you can easil
334
334
 
335
335
  ```python
336
336
  >>> def f(n): return list(range(n))
337
- >>> pt.config.set(f, "time", number=10_000, repeat=1)
337
+ >>> pt.config.set(f, "time", Number=10_000, Repeat=1)
338
338
 
339
339
  ```
340
340
 
@@ -347,7 +347,7 @@ When you use `perftester` as a command-line tool, you can modify `pt.config` in
347
347
  import perftester as pt
348
348
 
349
349
  # shorten the tests
350
- pt.config.set_defaults("time", number=10_000, repeat=3)
350
+ pt.config.set_defaults("time", Number=10_000, Repeat=3)
351
351
 
352
352
  # log the results to file (they will be printed in the console anyway)
353
353
  pt.config.log_to_file = True
@@ -0,0 +1,12 @@
1
+ perftester/__init__.py,sha256=x9O2UW8vfTuF7uKzPUeZ397FEzjcv42X2H26je9cyOI,273
2
+ perftester/__main__.py,sha256=aX_J60lLY2yoz5jXtNoqTnAE_wm_s4llQHeR3z2Mx68,5119
3
+ perftester/perftester.py,sha256=q8fj5hl0vW5rmB5FmdVqKZ5Vk43SkMgsC8tLX8m77Gw,31559
4
+ perftester/tmp.py,sha256=jqCDGCRO5fv7Uv7cJLu4PNmnaUMIApuu3nT_2zQwWqQ,1657
5
+ perftester/tmp_working.py,sha256=7ub5M6PFFfhSTCp_a-YQOZSFD05VNxa8Y7tDnEcBZzk,820
6
+ perftester/understand.py,sha256=H70Yjt3MPSB8rSjEi88Rwik15ZaVKn7OFBizC5H72NA,15670
7
+ perftester-0.5.1.dist-info/LICENSE,sha256=mZFAdfuYFAyBYiir4m3CTQu151mpXbMbh7Mm5M1bZAE,1063
8
+ perftester-0.5.1.dist-info/METADATA,sha256=gieG4jHDVbnwmfPwh_RB8jRlWnHPvjtiQ06SmfP1lgM,24670
9
+ perftester-0.5.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
10
+ perftester-0.5.1.dist-info/entry_points.txt,sha256=gM6Vf1BEeLY-1X9IQlO1TPAO3lJ5vToKfnJHT4MruIk,57
11
+ perftester-0.5.1.dist-info/top_level.txt,sha256=i1-4oWlkta2MsNKlZwJCibhn7aBexQfxncoPy2a6dfA,11
12
+ perftester-0.5.1.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- perftester/__init__.py,sha256=x9O2UW8vfTuF7uKzPUeZ397FEzjcv42X2H26je9cyOI,273
2
- perftester/__main__.py,sha256=aX_J60lLY2yoz5jXtNoqTnAE_wm_s4llQHeR3z2Mx68,5119
3
- perftester/perftester.py,sha256=gSM59rgPta5KXgsjzOcbEvPnAmBZhlvArnNsxR5SgBw,30965
4
- perftester-0.4.0.dist-info/LICENSE,sha256=mZFAdfuYFAyBYiir4m3CTQu151mpXbMbh7Mm5M1bZAE,1063
5
- perftester-0.4.0.dist-info/METADATA,sha256=v6roxxBxpqZNXC1SbuYrIUJVbR-_c0I2kctz2GqzRtE,24692
6
- perftester-0.4.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
7
- perftester-0.4.0.dist-info/entry_points.txt,sha256=gM6Vf1BEeLY-1X9IQlO1TPAO3lJ5vToKfnJHT4MruIk,57
8
- perftester-0.4.0.dist-info/top_level.txt,sha256=i1-4oWlkta2MsNKlZwJCibhn7aBexQfxncoPy2a6dfA,11
9
- perftester-0.4.0.dist-info/RECORD,,