Path: blob/main/singlestoredb/functions/ext/timer.py
469 views
import json1import time2from typing import Any3from typing import Dict4from typing import Optional567class RoundedFloatEncoder(json.JSONEncoder):89def encode(self, obj: Any) -> str:10if isinstance(obj, dict):11return '{' + ', '.join(12f'"{k}": {self._format_value(v)}'13for k, v in obj.items()14) + '}'15return super().encode(obj)1617def _format_value(self, value: Any) -> str:18if isinstance(value, float):19return f'{value:.2f}'20return json.dumps(value)212223class Timer:24"""25Timer context manager that supports nested timing using a stack.2627Example28-------29timer = Timer()3031with timer('total'):32with timer('receive_data'):33time.sleep(0.1)34with timer('parse_input'):35time.sleep(0.2)36with timer('call_function'):37with timer('inner_operation'):38time.sleep(0.05)39time.sleep(0.3)4041print(timer.metrics)42# {'receive_data': 0.1, 'parse_input': 0.2, 'inner_operation': 0.05,43# 'call_function': 0.35, 'total': 0.65}4445"""4647def __init__(self, **kwargs: Any) -> None:48self.metadata: Dict[str, Any] = kwargs49self.metrics: Dict[str, float] = dict()50self.entries: Dict[str, float] = dict()51self._current_key: Optional[str] = None52self.start_time = time.perf_counter()5354def __call__(self, key: str) -> 'Timer':55self._current_key = key56return self5758def __enter__(self) -> 'Timer':59if self._current_key is None:60raise ValueError(61"No key specified. Use timer('key_name') as context manager.",62)63self.entries[self._current_key] = time.perf_counter()64return self6566def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:67key = self._current_key68if key and key in self.entries:69start = self.entries.pop(key)70elapsed = time.perf_counter() - start71self.metrics[key] = elapsed72self._current_key = None7374async def __aenter__(self) -> 'Timer':75return self.__enter__()7677async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:78self.__exit__(exc_type, exc_val, exc_tb)7980def reset(self) -> None:81self.metrics.clear()82self.entries.clear()83self._current_key = None8485def finish(self) -> Dict[str, Any]:86"""Finish the current timing context and store the elapsed time."""87self.metrics['total'] = time.perf_counter() - self.start_time88return dict(type='function_metrics', **self.metadata, **self.metrics)899091