2024-11-25 16:40:36 -03:00
|
|
|
import os
|
2024-10-30 20:22:09 -03:00
|
|
|
import random
|
2024-11-25 16:40:36 -03:00
|
|
|
import tempfile
|
2024-11-25 16:08:22 -03:00
|
|
|
from abc import ABC, abstractmethod
|
2024-11-25 19:03:54 -03:00
|
|
|
from contextlib import contextmanager, AbstractContextManager
|
2024-11-25 16:08:22 -03:00
|
|
|
from dataclasses import dataclass
|
2024-10-30 20:22:09 -03:00
|
|
|
from pathlib import Path
|
2024-12-03 17:50:27 -03:00
|
|
|
from time import time, sleep
|
|
|
|
from typing import Iterator, Tuple, ContextManager, Optional, Callable
|
2024-10-30 20:22:09 -03:00
|
|
|
|
2024-11-25 16:08:22 -03:00
|
|
|
from typing_extensions import Generic
|
|
|
|
|
|
|
|
from benchmarks.core.network import TInitialMetadata
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
2024-11-25 19:03:54 -03:00
|
|
|
class ExperimentData(Generic[TInitialMetadata], AbstractContextManager, ABC):
|
|
|
|
""":class:`ExperimentData` provides a context for providing and wiping out
|
|
|
|
data and metadata objects, usually within the scope of an experiment. """
|
2024-11-25 16:08:22 -03:00
|
|
|
|
2024-11-25 19:03:54 -03:00
|
|
|
@abstractmethod
|
|
|
|
def __enter__(self) -> Tuple[TInitialMetadata, Path]:
|
|
|
|
"""Generates new data and metadata and returns it."""
|
|
|
|
pass
|
2024-11-25 16:08:22 -03:00
|
|
|
|
|
|
|
@abstractmethod
|
2024-11-25 19:03:54 -03:00
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
|
|
"""Wipes out data and metadata."""
|
2024-11-25 16:08:22 -03:00
|
|
|
pass
|
2024-10-30 20:22:09 -03:00
|
|
|
|
|
|
|
|
2024-11-25 19:03:54 -03:00
|
|
|
class RandomTempData(ExperimentData[TInitialMetadata]):
|
|
|
|
|
|
|
|
def __init__(self, size: int, meta: TInitialMetadata):
|
|
|
|
self.meta = meta
|
|
|
|
self.size = size
|
|
|
|
self._context: Optional[ContextManager[Tuple[TInitialMetadata, Path]]] = None
|
|
|
|
|
|
|
|
def __enter__(self) -> Tuple[TInitialMetadata, Path]:
|
|
|
|
if self._context is not None:
|
|
|
|
raise Exception('Cannot enter context twice')
|
|
|
|
|
|
|
|
self._context = temp_random_file(self.size, 'data.bin')
|
|
|
|
|
|
|
|
return self.meta, self._context.__enter__()
|
|
|
|
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
|
|
self._context.__exit__(exc_type, exc_val, exc_tb)
|
|
|
|
|
|
|
|
|
2024-11-25 16:40:36 -03:00
|
|
|
@contextmanager
|
|
|
|
def temp_random_file(size: int, name: str = 'data.bin'):
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir_str:
|
|
|
|
temp_dir = Path(temp_dir_str)
|
|
|
|
random_file = temp_dir / name
|
|
|
|
random_bytes = os.urandom(size)
|
|
|
|
with random_file.open('wb') as outfile:
|
|
|
|
outfile.write(random_bytes)
|
|
|
|
|
|
|
|
yield random_file
|
|
|
|
|
|
|
|
|
2024-12-03 17:50:27 -03:00
|
|
|
def await_predicate(predicate: Callable[[], bool], timeout: float = 0, polling_interval: float = 0) -> bool:
|
|
|
|
current = time()
|
|
|
|
while (timeout == 0) or ((time() - current) <= timeout):
|
|
|
|
if predicate():
|
|
|
|
return True
|
|
|
|
sleep(polling_interval)
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2024-10-30 20:22:09 -03:00
|
|
|
def sample(n: int) -> Iterator[int]:
|
2024-11-01 18:07:08 -03:00
|
|
|
"""Samples without replacement using a basic Fisher-Yates shuffle."""
|
2024-10-30 20:22:09 -03:00
|
|
|
p = list(range(0, n))
|
|
|
|
for i in range(n - 1):
|
2024-11-27 11:36:17 -03:00
|
|
|
j = random.randint(i, n - 1)
|
2024-10-30 20:22:09 -03:00
|
|
|
tmp = p[j]
|
2024-11-27 11:22:07 -03:00
|
|
|
p[j] = p[i]
|
|
|
|
p[i] = tmp
|
2024-10-30 20:22:09 -03:00
|
|
|
yield p[i]
|
2024-11-01 18:07:08 -03:00
|
|
|
|
|
|
|
|
|
|
|
def kilobytes(n: int) -> int:
|
|
|
|
return n * 1024
|
|
|
|
|
|
|
|
|
|
|
|
def megabytes(n: int) -> int:
|
|
|
|
return kilobytes(n) * 1024
|