Keep K3 suite selection and report-schema scoring in bash, merge K3/vision dataset_args into dpv4 yamls, and pin EvalScope at 735d920ee911 with local patches. Co-authored-by: Cursor <cursoragent@cursor.com>
205 lines
6.7 KiB
Python
205 lines
6.7 KiB
Python
"""Tests for dataset loader cache isolation, media handling, and limits."""
|
|
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
import pyarrow as pa
|
|
import pytest
|
|
from datasets import Dataset as HFDataset
|
|
from datasets import DatasetInfo, Features, Value, Video
|
|
from datasets.table import InMemoryTable
|
|
|
|
from evalscope.api.dataset import Sample
|
|
from evalscope.api.dataset.hub import DatasetHub
|
|
from evalscope.api.dataset.loader import DictDataLoader, LocalDataLoader, RemoteDataLoader
|
|
from evalscope.constants import HubType
|
|
|
|
|
|
def _sample_from_record(record: Dict[str, Any]) -> Sample:
|
|
return Sample(input=record['text'], metadata={'video': record.get('video')})
|
|
|
|
|
|
def _install_fake_remote_dataset(monkeypatch: Any) -> None:
|
|
def fake_load(self: DatasetHub, split: str, subset: str = 'default', **kwargs: Any) -> HFDataset:
|
|
return HFDataset.from_dict({'text': [f'{subset}-{split}']})
|
|
|
|
monkeypatch.setattr(DatasetHub, 'load', fake_load)
|
|
|
|
|
|
def _cache_directories(dataset_dir: Path) -> List[Path]:
|
|
cache_root = dataset_dir / 'datasets'
|
|
return sorted(path for path in cache_root.iterdir() if path.is_dir())
|
|
|
|
|
|
def _load_remote_cache(
|
|
dataset_dir: Path,
|
|
*,
|
|
data_source: str,
|
|
split: str,
|
|
subset: str,
|
|
loader_kwargs: Optional[Dict[str, Any]] = None,
|
|
) -> None:
|
|
RemoteDataLoader(
|
|
data_id_or_path='dummy/cache-dataset',
|
|
split=split,
|
|
subset=subset,
|
|
sample_fields=_sample_from_record,
|
|
data_source=data_source,
|
|
dataset_dir=str(dataset_dir),
|
|
**(loader_kwargs or {}),
|
|
).load()
|
|
|
|
|
|
def test_remote_cache_directory_separates_data_sources(monkeypatch: Any, tmp_path: Path) -> None:
|
|
_install_fake_remote_dataset(monkeypatch)
|
|
|
|
_load_remote_cache(tmp_path, data_source=HubType.MODELSCOPE, split='train', subset='default')
|
|
_load_remote_cache(tmp_path, data_source=HubType.HUGGINGFACE, split='train', subset='default')
|
|
|
|
assert len(_cache_directories(tmp_path)) == 2
|
|
|
|
|
|
def test_remote_cache_directory_preserves_split_subset_boundaries(monkeypatch: Any, tmp_path: Path) -> None:
|
|
_install_fake_remote_dataset(monkeypatch)
|
|
|
|
_load_remote_cache(tmp_path, data_source=HubType.MODELSCOPE, split='a', subset='bc')
|
|
_load_remote_cache(tmp_path, data_source=HubType.MODELSCOPE, split='ab', subset='c')
|
|
|
|
assert len(_cache_directories(tmp_path)) == 2
|
|
|
|
|
|
def test_remote_cache_directory_is_stable_across_kwargs_order(monkeypatch: Any, tmp_path: Path) -> None:
|
|
_install_fake_remote_dataset(monkeypatch)
|
|
|
|
_load_remote_cache(
|
|
tmp_path,
|
|
data_source=HubType.MODELSCOPE,
|
|
split='train',
|
|
subset='default',
|
|
loader_kwargs={'b': 2, 'a': 1},
|
|
)
|
|
_load_remote_cache(
|
|
tmp_path,
|
|
data_source=HubType.MODELSCOPE,
|
|
split='train',
|
|
subset='default',
|
|
loader_kwargs={'a': 1, 'b': 2},
|
|
)
|
|
|
|
assert len(_cache_directories(tmp_path)) == 1
|
|
|
|
|
|
def test_remote_loader_treats_existing_path_as_effective_local_source(monkeypatch: Any, tmp_path: Path) -> None:
|
|
local_path = tmp_path / 'local-dataset'
|
|
local_path.mkdir()
|
|
dataset_dir = tmp_path / 'cache'
|
|
captured: Dict[str, Any] = {}
|
|
|
|
def fake_load(self: DatasetHub, split: str, subset: str = 'default', **kwargs: Any) -> HFDataset:
|
|
captured['data_source'] = self.data_source
|
|
return HFDataset.from_dict({'text': ['local']})
|
|
|
|
monkeypatch.setattr(DatasetHub, 'load', fake_load)
|
|
|
|
dataset = RemoteDataLoader(
|
|
data_id_or_path=str(local_path),
|
|
split='train',
|
|
sample_fields=_sample_from_record,
|
|
data_source=HubType.MODELSCOPE,
|
|
dataset_dir=str(dataset_dir),
|
|
).load()
|
|
|
|
assert len(dataset) == 1
|
|
assert captured['data_source'] == HubType.LOCAL
|
|
assert not (dataset_dir / 'datasets').exists()
|
|
|
|
|
|
def test_remote_loader_does_not_decode_video(monkeypatch: Any, tmp_path: Path) -> None:
|
|
features = Features({'video': Video(decode=True), 'text': Value('string')})
|
|
row = {'video': {'bytes': b'dummy', 'path': None}, 'text': 'clip'}
|
|
table = pa.Table.from_pylist([row], schema=features.arrow_schema)
|
|
remote_dataset = HFDataset(InMemoryTable(table), info=DatasetInfo(features=features))
|
|
|
|
def fake_load(self: DatasetHub, split: str, subset: str = 'default', **kwargs: Any) -> HFDataset:
|
|
return remote_dataset
|
|
|
|
monkeypatch.setattr(DatasetHub, 'load', fake_load)
|
|
|
|
dataset = RemoteDataLoader(
|
|
data_id_or_path='dummy/video-dataset',
|
|
split='train',
|
|
sample_fields=_sample_from_record,
|
|
data_source=HubType.LOCAL,
|
|
dataset_dir=str(tmp_path),
|
|
).load()
|
|
|
|
assert dataset[0].metadata['video'] == {'bytes': b'dummy', 'path': None}
|
|
|
|
|
|
def _make_direct_loader(
|
|
loader_kind: str,
|
|
tmp_path: Path,
|
|
monkeypatch: Any,
|
|
limit: Optional[float | int],
|
|
) -> RemoteDataLoader | LocalDataLoader | DictDataLoader:
|
|
records = [{'text': f'row-{index}'} for index in range(4)]
|
|
if loader_kind == 'remote':
|
|
monkeypatch.setattr(DatasetHub, 'load', lambda self, split, subset='default', **kwargs: HFDataset.from_list(records))
|
|
return RemoteDataLoader(
|
|
data_id_or_path='dummy/limit-dataset',
|
|
split='train',
|
|
sample_fields=_sample_from_record,
|
|
data_source=HubType.LOCAL,
|
|
dataset_dir=str(tmp_path),
|
|
limit=limit,
|
|
)
|
|
if loader_kind == 'local':
|
|
local_path = tmp_path / 'records.jsonl'
|
|
local_path.write_text('\n'.join(json.dumps(record) for record in records), encoding='utf-8')
|
|
return LocalDataLoader(
|
|
data_id_or_path=str(local_path),
|
|
split='train',
|
|
sample_fields=_sample_from_record,
|
|
limit=limit,
|
|
)
|
|
return DictDataLoader(dict_list=records, sample_fields=_sample_from_record, limit=limit)
|
|
|
|
|
|
@pytest.mark.parametrize('loader_kind', ['remote', 'local', 'dict'])
|
|
@pytest.mark.parametrize('limit', [2.5, -1, -0.1, math.nan, math.inf])
|
|
def test_direct_loaders_reject_invalid_limits(
|
|
loader_kind: str,
|
|
limit: float | int,
|
|
monkeypatch: Any,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
with pytest.raises(ValueError, match='Limit must'):
|
|
_make_direct_loader(loader_kind, tmp_path, monkeypatch, limit).load()
|
|
|
|
|
|
@pytest.mark.parametrize('loader_kind', ['remote', 'local', 'dict'])
|
|
@pytest.mark.parametrize(
|
|
'limit, expected',
|
|
[
|
|
(None, 4),
|
|
(0, 4),
|
|
(0.5, 2),
|
|
(1.0, 4),
|
|
(2, 2),
|
|
],
|
|
)
|
|
def test_direct_loaders_preserve_valid_limit_semantics(
|
|
loader_kind: str,
|
|
limit: Optional[float | int],
|
|
expected: int,
|
|
monkeypatch: Any,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
loader = _make_direct_loader(loader_kind, tmp_path, monkeypatch, limit)
|
|
|
|
assert len(loader.load()) == expected
|
|
assert len(loader.load()) == expected
|
|
assert loader.limit == limit
|