Skip to content

Commit 06c1959

Browse files
committed
Add Python decorators for performance testing
1 parent db0ac9b commit 06c1959

File tree

1 file changed

+109
-0
lines changed

1 file changed

+109
-0
lines changed

seleniumbase/common/decorators.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,118 @@
33
import sys
44
import time
55
import warnings
6+
from contextlib import contextmanager
67
from functools import wraps
78

89

10+
@contextmanager
11+
def print_runtime(description=None, limit=None):
12+
"""Print the runtime duration of a method or "with"-block after completion.
13+
If limit, fail if the runtime duration exceeds the limit after completion.
14+
15+
Method / Function example usage ->
16+
from seleniumbase import decorators
17+
18+
@decorators.print_runtime("My Method")
19+
def my_method():
20+
# code ...
21+
# code ...
22+
23+
"with"-block example usage ->
24+
from seleniumbase import decorators
25+
26+
with decorators.print_runtime("My Code Block"):
27+
# code ...
28+
# code ...
29+
"""
30+
if not description:
31+
description = "Code Block"
32+
description = str(description)
33+
if limit:
34+
limit = float("%.2f" % limit)
35+
if limit < 0.01:
36+
limit = 0.01 # Minimum runtime limit
37+
exception = None
38+
start_time = time.time()
39+
try:
40+
yield
41+
except Exception as e:
42+
exception = e
43+
raise
44+
finally:
45+
end_time = time.time()
46+
run_time = end_time - start_time
47+
# Print times with a statistically significant number of decimal places
48+
if run_time < 0.0001:
49+
print(" {%s} ran for %.7f seconds." % (description, run_time))
50+
elif run_time < 0.001:
51+
print(" {%s} ran for %.6f seconds." % (description, run_time))
52+
elif run_time < 0.01:
53+
print(" {%s} ran for %.5f seconds." % (description, run_time))
54+
elif run_time < 0.1:
55+
print(" {%s} ran for %.4f seconds." % (description, run_time))
56+
elif run_time < 1:
57+
print(" {%s} ran for %.3f seconds." % (description, run_time))
58+
else:
59+
print(" {%s} ran for %.2f seconds." % (description, run_time))
60+
if limit and limit > 0 and run_time > limit:
61+
message = (
62+
"\n {%s} duration of %.2fs exceeded the time limit of %.2fs!"
63+
% (description, run_time, limit)
64+
)
65+
if exception:
66+
message = exception.msg + "\nAND " + message
67+
raise Exception(message)
68+
69+
70+
@contextmanager
71+
def runtime_limit(limit, description=None):
72+
"""
73+
Fail if the runtime duration of a method or "with"-block exceeds the limit.
74+
(The failure won't occur until after the method or "with"-block completes.)
75+
76+
Method / Function example usage ->
77+
from seleniumbase import decorators
78+
79+
@decorators.runtime_limit(4.5)
80+
def my_method():
81+
# code ...
82+
# code ...
83+
84+
"with"-block example usage ->
85+
from seleniumbase import decorators
86+
87+
with decorators.runtime_limit(32):
88+
# code ...
89+
# code ...
90+
"""
91+
limit = float("%.2f" % limit)
92+
if limit < 0.01:
93+
limit = 0.01 # Minimum runtime limit
94+
if not description:
95+
description = "Code Block"
96+
description = str(description)
97+
exception = None
98+
start_time = time.time()
99+
try:
100+
yield
101+
except Exception as e:
102+
exception = e
103+
raise
104+
finally:
105+
end_time = time.time()
106+
run_time = end_time - start_time
107+
# Fail if the runtime of the code block exceeds the limit
108+
if limit and limit > 0 and run_time > limit:
109+
message = (
110+
"\n {%s} duration of %.2fs exceeded the time limit of %.2fs!"
111+
% (description, run_time, limit)
112+
)
113+
if exception:
114+
message = exception.msg + "\nAND " + message
115+
raise Exception(message)
116+
117+
9118
def retry_on_exception(tries=6, delay=1, backoff=2, max_delay=32):
10119
"""
11120
Decorator for implementing exponential backoff for retrying on failures.

0 commit comments

Comments
 (0)