Source code for ci_watson.resource_tracker
"""Resource tracking regtest utilities.
Can be used within module-scoped fixtures (often used to
run Steps or Pipelines) or within tests.
For uses where the resource usage occurs within a test:
.. code-block:: python
def test_long_step(resource_tracker, request):
with resource_tracker.track(log=request):
# something that takes memory and time
pass
For a module-scoped fixture the resource tracking can
be performed in the fixture but the logging/reporting of
the resource usage must occur during a test:
.. code-block:: python
@pytest.fixture(scope="module")
def resource_tracker():
return ResourceTracker()
@pytest.fixture()
def log_tracked_resources(resource_tracker, request):
def callback():
resource_tracker.log(request)
yield callback
@pytest.fixture
def my_long_fixture(resource_tracker):
with resource_tracker.track():
# something that takes memory and time
pass
def test_log_tracked_resources(log_tracked_resources, my_long_fixture):
log_tracked_resources()
Use of the module-scoped fixture has fixture-reuse
considerations similar to the ``rtdata_module`` fixture. Having
more than one module scoped fixture that uses ``resource_tracker``
per module is discouraged (as both will use the same ``ResourceTracker``
instance). Parameterization of a fixture using ``resource_tracker``
is supported (same as ``rtdata_module``).
"""
import time
import tracemalloc
from contextlib import ExitStack, contextmanager
__all__ = ["ResourceTracker"]
class _TrackRuntime:
"""Runtime tracker context."""
def __enter__(self):
self._t0 = time.monotonic()
def __exit__(self, exc_type, exc_value, traceback):
self.value = time.monotonic() - self._t0
def log(self):
return ("tracked-time", self.value)
class _TrackPeakMemory:
"""Peak memory tracker context."""
def __enter__(self):
tracemalloc.start()
def __exit__(self, exc_type, exc_value, traceback):
_, self.value = tracemalloc.get_traced_memory()
tracemalloc.stop()
def log(self):
return ("tracked-peakmem", self.value)
[docs]
class ResourceTracker:
"""Track resources used during track context."""
def __init__(self):
self._trackers = [_TrackPeakMemory(), _TrackRuntime()]
[docs]
def log(self, request):
"""Log tracked resource usage to the pytest request user properties.
Parameters
----------
request : pytest.FixtureRequest
Must be a function-scoped pytest request fixture result.
"""
request.node.user_properties.extend(t.log() for t in self._trackers)
[docs]
@contextmanager
def track(self, log=None):
"""Context during which resources are tracked.
Parameters
----------
log : pytest.FixtureRequest, optional
If provided, log the usage to the provided request fixture result.
"""
try:
with ExitStack() as stack:
[stack.enter_context(t) for t in self._trackers]
yield self
finally:
if log:
self.log(log)