Skip to content

Commit c645da2

Browse files
author
Lucas Haupt
committed
Add workaround fix for faulty memory_profiler module
The memory profiler only reports Exceptions when it should report all Exceptions that inherit from BaseExceptions. This is ultimately reworked in a PR incorporating a simplified memory profiler module into the codebase that fixes not only this issue but also gives the possibility of getting measurements for failing tests. This workaround uses return values to work around the issue. This way pytest-monitor can check for BaseExceptions and act accordingly. Like described earlier the ultimate goal should probably be to replace the whole memory profiler as proposed in this PR: CFMTech#82
1 parent d831b4f commit c645da2

File tree

1 file changed

+74
-22
lines changed

1 file changed

+74
-22
lines changed

pytest_monitor/pytest_monitor.py

+74-22
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
"monitor_test_if": (True, "monitor_force_test", lambda x: bool(x), False),
2323
}
2424
PYTEST_MONITOR_DEPRECATED_MARKERS = {}
25-
PYTEST_MONITOR_ITEM_LOC_MEMBER = "_location" if tuple(pytest.__version__.split(".")) < ("5", "3") else "location"
25+
PYTEST_MONITOR_ITEM_LOC_MEMBER = (
26+
"_location" if tuple(pytest.__version__.split(".")) < ("5", "3") else "location"
27+
)
2628

2729
PYTEST_MONITORING_ENABLED = True
2830

@@ -44,7 +46,9 @@ def pytest_addoption(parser):
4446
help="Set this option to distinguish parametrized tests given their values."
4547
" This requires the parameters to be stringifiable.",
4648
)
47-
group.addoption("--no-monitor", action="store_true", dest="mtr_none", help="Disable all traces")
49+
group.addoption(
50+
"--no-monitor", action="store_true", dest="mtr_none", help="Disable all traces"
51+
)
4852
group.addoption(
4953
"--remote-server",
5054
action="store",
@@ -68,13 +72,15 @@ def pytest_addoption(parser):
6872
"--force-component",
6973
action="store",
7074
dest="mtr_force_component",
71-
help="Force the component to be set at the given value for the all tests run" " in this session.",
75+
help="Force the component to be set at the given value for the all tests run"
76+
" in this session.",
7277
)
7378
group.addoption(
7479
"--component-prefix",
7580
action="store",
7681
dest="mtr_component_prefix",
77-
help="Prefix each found components with the given value (applies to all tests" " run in this session).",
82+
help="Prefix each found components with the given value (applies to all tests"
83+
" run in this session).",
7884
)
7985
group.addoption(
8086
"--no-gc",
@@ -99,10 +105,13 @@ def pytest_addoption(parser):
99105

100106

101107
def pytest_configure(config):
102-
config.addinivalue_line("markers", "monitor_skip_test: mark test to be executed but not monitored.")
108+
config.addinivalue_line(
109+
"markers", "monitor_skip_test: mark test to be executed but not monitored."
110+
)
103111
config.addinivalue_line(
104112
"markers",
105-
"monitor_skip_test_if(cond): mark test to be executed but " "not monitored if cond is verified.",
113+
"monitor_skip_test_if(cond): mark test to be executed but "
114+
"not monitored if cond is verified.",
106115
)
107116
config.addinivalue_line(
108117
"markers",
@@ -126,14 +135,24 @@ def pytest_runtest_setup(item):
126135
"""
127136
if not PYTEST_MONITORING_ENABLED:
128137
return
129-
item_markers = {mark.name: mark for mark in item.iter_markers() if mark and mark.name.startswith("monitor_")}
138+
item_markers = {
139+
mark.name: mark
140+
for mark in item.iter_markers()
141+
if mark and mark.name.startswith("monitor_")
142+
}
130143
mark_to_del = []
131144
for set_marker in item_markers.keys():
132145
if set_marker not in PYTEST_MONITOR_VALID_MARKERS:
133-
warnings.warn("Nothing known about marker {}. Marker will be dropped.".format(set_marker))
146+
warnings.warn(
147+
"Nothing known about marker {}. Marker will be dropped.".format(
148+
set_marker
149+
)
150+
)
134151
mark_to_del.append(set_marker)
135152
if set_marker in PYTEST_MONITOR_DEPRECATED_MARKERS:
136-
warnings.warn(f"Marker {set_marker} is deprecated. Consider upgrading your tests")
153+
warnings.warn(
154+
f"Marker {set_marker} is deprecated. Consider upgrading your tests"
155+
)
137156

138157
for marker in mark_to_del:
139158
del item_markers[marker]
@@ -201,11 +220,20 @@ def wrapped_function():
201220
pyfuncitem.obj(**testargs)
202221
except Exception:
203222
raise
204-
except BaseException:
205-
raise
223+
except BaseException as e:
224+
# this is a workaround to fix the faulty behavior of the memory profiler
225+
# that only catches Exceptions but should catch BaseExceptions instead
226+
# actually BaseExceptions should be raised here, but without modifications
227+
# of the memory profiler (like proposed in PR
228+
# https://github.com/CFMTech/pytest-monitor/pull/82 ) this problem
229+
# can just be worked around like so (BaseException can only come through
230+
# this way)
231+
return e
206232

207233
def prof():
208-
m = memory_profiler.memory_usage((wrapped_function, ()), max_iterations=1, max_usage=True, retval=True)
234+
m = memory_profiler.memory_usage(
235+
(wrapped_function, ()), max_iterations=1, max_usage=True, retval=True
236+
)
209237
if isinstance(m[1], BaseException): # Do we have any outcome?
210238
raise m[1]
211239
memuse = m[0][0] if type(m[0]) is list else m[0]
@@ -214,9 +242,13 @@ def prof():
214242

215243
if not PYTEST_MONITORING_ENABLED:
216244
try:
217-
wrapped_function()
245+
# this is a workaround to fix the faulty behavior of the memory profiler
246+
# that only catches Exceptions but should catch BaseExceptions instead
247+
e = wrapped_function()
248+
if isinstance(e, BaseException):
249+
raise e
218250
except BaseException:
219-
raise
251+
raise
220252
else:
221253
if not pyfuncitem.session.config.option.mtr_disable_gc:
222254
gc.collect()
@@ -236,12 +268,26 @@ def pytest_sessionstart(session):
236268
Instantiate a monitor session to save collected metrics.
237269
We yield at the end to let pytest pursue the execution.
238270
"""
239-
if session.config.option.mtr_force_component and session.config.option.mtr_component_prefix:
240-
raise pytest.UsageError("Invalid usage: --force-component and --component-prefix are incompatible options!")
241-
if session.config.option.mtr_no_db and not session.config.option.mtr_remote and not session.config.option.mtr_none:
242-
warnings.warn("pytest-monitor: No storage specified but monitoring is requested. Disabling monitoring.")
271+
if (
272+
session.config.option.mtr_force_component
273+
and session.config.option.mtr_component_prefix
274+
):
275+
raise pytest.UsageError(
276+
"Invalid usage: --force-component and --component-prefix are incompatible options!"
277+
)
278+
if (
279+
session.config.option.mtr_no_db
280+
and not session.config.option.mtr_remote
281+
and not session.config.option.mtr_none
282+
):
283+
warnings.warn(
284+
"pytest-monitor: No storage specified but monitoring is requested. Disabling monitoring."
285+
)
243286
session.config.option.mtr_none = True
244-
component = session.config.option.mtr_force_component or session.config.option.mtr_component_prefix
287+
component = (
288+
session.config.option.mtr_force_component
289+
or session.config.option.mtr_component_prefix
290+
)
245291
if session.config.option.mtr_component_prefix:
246292
component += ".{user_component}"
247293
if not component:
@@ -251,13 +297,17 @@ def pytest_sessionstart(session):
251297
if (session.config.option.mtr_none or session.config.option.mtr_no_db)
252298
else session.config.option.mtr_db_out
253299
)
254-
remote = None if session.config.option.mtr_none else session.config.option.mtr_remote
300+
remote = (
301+
None if session.config.option.mtr_none else session.config.option.mtr_remote
302+
)
255303
session.pytest_monitor = PyTestMonitorSession(
256304
db=db, remote=remote, component=component, scope=session.config.option.mtr_scope
257305
)
258306
global PYTEST_MONITORING_ENABLED
259307
PYTEST_MONITORING_ENABLED = not session.config.option.mtr_none
260-
session.pytest_monitor.compute_info(session.config.option.mtr_description, session.config.option.mtr_tags)
308+
session.pytest_monitor.compute_info(
309+
session.config.option.mtr_description, session.config.option.mtr_tags
310+
)
261311
yield
262312

263313

@@ -298,7 +348,9 @@ def _prf_tracer(request):
298348
ptimes_a = request.session.pytest_monitor.process.cpu_times()
299349
yield
300350
ptimes_b = request.session.pytest_monitor.process.cpu_times()
301-
if not request.node.monitor_skip_test and getattr(request.node, "monitor_results", False):
351+
if not request.node.monitor_skip_test and getattr(
352+
request.node, "monitor_results", False
353+
):
302354
item_name = request.node.originalname or request.node.name
303355
item_loc = getattr(request.node, PYTEST_MONITOR_ITEM_LOC_MEMBER)[0]
304356
request.session.pytest_monitor.add_test_info(

0 commit comments

Comments
 (0)