-
Notifications
You must be signed in to change notification settings - Fork 1
/
pytest_memory_usage.py
122 lines (94 loc) · 3.24 KB
/
pytest_memory_usage.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# -*- coding: utf-8 -*-
import pytest
import _pytest.config
import os
import sys
import psutil
import gc
def pytest_addoption(parser):
group = parser.getgroup('memory-usage')
group.addoption(
'--memory-usage',
action='store_true',
default=False,
help='Report memory usage'
)
parser.addini('memory_usage', 'Report memory usage', type='bool', default=False)
configuration = None
writer = None
def pytest_configure(config):
global configuration
configuration = config
global writer
writer = _pytest.config.create_terminal_writer(config, sys.stdout)
class MemoryState(object):
def __init__(self):
self.clear()
def clear(self):
self.before_setup = None
self.before_call = None
self.after_setup = None
self.after_call = None
self.process = None
_TWO_20 = float(2 ** 20)
def get_memory(process, include_children=True):
"""Inspired by the memory_profiler module's implementation"""
try:
mem = process.memory_info()[0] / _TWO_20
if include_children:
for p in process.children(recursive=True):
mem += p.memory_info()[0] / _TWO_20
return mem
except psutil.AccessDenied:
return None
def get_process():
pid = os.getpid()
return psutil.Process(pid)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(item):
if configuration.getoption('memory_usage') or configuration.getini('memory_usage'):
state.clear()
state.process = get_process()
gc.disable()
state.before_setup = get_memory(state.process)
yield
state.after_setup = get_memory(state.process)
gc.enable()
else:
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
if configuration.getoption('memory_usage') or configuration.getini('memory_usage'):
gc.disable()
state.before_call = get_memory(state.process)
yield
state.after_call = get_memory(state.process)
gc.enable()
else:
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item):
if configuration.getoption('memory_usage') or configuration.getini('memory_usage'):
outcome = yield
report = outcome.get_result()
memory_usage = 0
if (state.before_setup is not None and
state.after_setup is not None):
memory_usage += state.after_setup - state.before_setup
if (state.before_call is not None and
state.after_call is not None):
memory_usage += state.after_call - state.before_call
report.__dict__.update(dict(memory_usage=memory_usage))
else:
yield
@pytest.hookimpl(trylast=True)
def pytest_runtest_logreport(report):
if configuration.getoption('memory_usage') or configuration.getini('memory_usage'):
if report.when == 'call' and report.passed:
if hasattr(report, 'memory_usage'):
writer.write(' ({memory_usage:.0f}MB'.format(memory_usage=report.memory_usage))
if report.memory_usage < 0:
writer.write(' - gc.collect() probably called explicitly')
writer.write(')')
return None
state = MemoryState()