sci-modeling-bench 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sci_modeling_bench/__init__.py +63 -0
- sci_modeling_bench/dataset/__init__.py +46 -0
- sci_modeling_bench/dataset/_hub.py +103 -0
- sci_modeling_bench/dataset/dataset.py +291 -0
- sci_modeling_bench/dataset/knowledge.py +44 -0
- sci_modeling_bench/dataset/manifest.py +217 -0
- sci_modeling_bench/dataset/metadata.py +38 -0
- sci_modeling_bench/dataset/schema.py +148 -0
- sci_modeling_bench/dataset/source.py +22 -0
- sci_modeling_bench/dataset/validation.py +306 -0
- sci_modeling_bench/exceptions.py +45 -0
- sci_modeling_bench/objective/__init__.py +9 -0
- sci_modeling_bench/objective/objective.py +96 -0
- sci_modeling_bench/protocol/__init__.py +5 -0
- sci_modeling_bench/protocol/protocol.py +25 -0
- sci_modeling_bench/py.typed +1 -0
- sci_modeling_bench/suites/__init__.py +2 -0
- sci_modeling_bench/suites/design_bench/__init__.py +21 -0
- sci_modeling_bench/suites/design_bench/tfbind8/__init__.py +25 -0
- sci_modeling_bench/suites/design_bench/tfbind8/build.py +600 -0
- sci_modeling_bench/suites/design_bench/tfbind8/dataset.py +212 -0
- sci_modeling_bench/suites/design_bench/tfbind8/objective.py +101 -0
- sci_modeling_bench/suites/design_bench/tfbind8/protocol.py +100 -0
- sci_modeling_bench-0.1.0.dist-info/METADATA +128 -0
- sci_modeling_bench-0.1.0.dist-info/RECORD +29 -0
- sci_modeling_bench-0.1.0.dist-info/WHEEL +5 -0
- sci_modeling_bench-0.1.0.dist-info/entry_points.txt +2 -0
- sci_modeling_bench-0.1.0.dist-info/licenses/LICENSE +21 -0
- sci_modeling_bench-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""SciModelingBench public package."""
|
|
2
|
+
|
|
3
|
+
from sci_modeling_bench.dataset import (
|
|
4
|
+
AllowedValuesConstraint,
|
|
5
|
+
AlphabetConstraint,
|
|
6
|
+
Dataset,
|
|
7
|
+
DatasetMetadata,
|
|
8
|
+
DatasetSchema,
|
|
9
|
+
DatasetSplit,
|
|
10
|
+
DatasetValidator,
|
|
11
|
+
FieldSpec,
|
|
12
|
+
FiniteConstraint,
|
|
13
|
+
HubDatasetSource,
|
|
14
|
+
Knowledge,
|
|
15
|
+
KnowledgeResource,
|
|
16
|
+
LengthConstraint,
|
|
17
|
+
NumericRangeConstraint,
|
|
18
|
+
ValidationReport,
|
|
19
|
+
Violation,
|
|
20
|
+
)
|
|
21
|
+
from sci_modeling_bench.exceptions import (
|
|
22
|
+
DatasetLoadError,
|
|
23
|
+
ManifestError,
|
|
24
|
+
ObjectiveError,
|
|
25
|
+
ObjectiveInputError,
|
|
26
|
+
ObjectiveLookupError,
|
|
27
|
+
ObjectiveOutputError,
|
|
28
|
+
ProtocolError,
|
|
29
|
+
SchemaMismatchError,
|
|
30
|
+
SciModelingBenchError,
|
|
31
|
+
)
|
|
32
|
+
from sci_modeling_bench.objective import Objective
|
|
33
|
+
from sci_modeling_bench.protocol import Protocol
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"AllowedValuesConstraint",
|
|
37
|
+
"AlphabetConstraint",
|
|
38
|
+
"Dataset",
|
|
39
|
+
"DatasetLoadError",
|
|
40
|
+
"DatasetMetadata",
|
|
41
|
+
"DatasetSchema",
|
|
42
|
+
"DatasetSplit",
|
|
43
|
+
"DatasetValidator",
|
|
44
|
+
"FieldSpec",
|
|
45
|
+
"FiniteConstraint",
|
|
46
|
+
"HubDatasetSource",
|
|
47
|
+
"Knowledge",
|
|
48
|
+
"KnowledgeResource",
|
|
49
|
+
"LengthConstraint",
|
|
50
|
+
"ManifestError",
|
|
51
|
+
"NumericRangeConstraint",
|
|
52
|
+
"Objective",
|
|
53
|
+
"ObjectiveError",
|
|
54
|
+
"ObjectiveInputError",
|
|
55
|
+
"ObjectiveLookupError",
|
|
56
|
+
"ObjectiveOutputError",
|
|
57
|
+
"Protocol",
|
|
58
|
+
"ProtocolError",
|
|
59
|
+
"SchemaMismatchError",
|
|
60
|
+
"SciModelingBenchError",
|
|
61
|
+
"ValidationReport",
|
|
62
|
+
"Violation",
|
|
63
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Dataset loading, schema, validation, and knowledge resources."""
|
|
2
|
+
|
|
3
|
+
from sci_modeling_bench.dataset.dataset import Dataset
|
|
4
|
+
from sci_modeling_bench.dataset.knowledge import Knowledge, KnowledgeResource
|
|
5
|
+
from sci_modeling_bench.dataset.manifest import DatasetSplit
|
|
6
|
+
from sci_modeling_bench.dataset.metadata import (
|
|
7
|
+
Citation,
|
|
8
|
+
DatasetMetadata,
|
|
9
|
+
SourceReference,
|
|
10
|
+
)
|
|
11
|
+
from sci_modeling_bench.dataset.schema import (
|
|
12
|
+
AllowedValuesConstraint,
|
|
13
|
+
AlphabetConstraint,
|
|
14
|
+
DatasetSchema,
|
|
15
|
+
FieldSpec,
|
|
16
|
+
FiniteConstraint,
|
|
17
|
+
LengthConstraint,
|
|
18
|
+
NumericRangeConstraint,
|
|
19
|
+
)
|
|
20
|
+
from sci_modeling_bench.dataset.source import HubDatasetSource
|
|
21
|
+
from sci_modeling_bench.dataset.validation import (
|
|
22
|
+
DatasetValidator,
|
|
23
|
+
ValidationReport,
|
|
24
|
+
Violation,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"AllowedValuesConstraint",
|
|
29
|
+
"AlphabetConstraint",
|
|
30
|
+
"Citation",
|
|
31
|
+
"Dataset",
|
|
32
|
+
"DatasetMetadata",
|
|
33
|
+
"DatasetSchema",
|
|
34
|
+
"DatasetSplit",
|
|
35
|
+
"DatasetValidator",
|
|
36
|
+
"FieldSpec",
|
|
37
|
+
"FiniteConstraint",
|
|
38
|
+
"HubDatasetSource",
|
|
39
|
+
"Knowledge",
|
|
40
|
+
"KnowledgeResource",
|
|
41
|
+
"LengthConstraint",
|
|
42
|
+
"NumericRangeConstraint",
|
|
43
|
+
"SourceReference",
|
|
44
|
+
"ValidationReport",
|
|
45
|
+
"Violation",
|
|
46
|
+
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Internal Hugging Face repository adapter."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Protocol
|
|
8
|
+
|
|
9
|
+
from datasets import load_dataset
|
|
10
|
+
from huggingface_hub import HfApi, hf_hub_download
|
|
11
|
+
|
|
12
|
+
from sci_modeling_bench.exceptions import DatasetLoadError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DatasetRepository(Protocol):
|
|
16
|
+
"""Internal resource interface shared by Dataset and Knowledge."""
|
|
17
|
+
|
|
18
|
+
repo_id: str
|
|
19
|
+
resolved_revision: str
|
|
20
|
+
|
|
21
|
+
def read_text(self, path: str) -> str: ...
|
|
22
|
+
|
|
23
|
+
def load(
|
|
24
|
+
self,
|
|
25
|
+
config_name: str,
|
|
26
|
+
split: str,
|
|
27
|
+
*,
|
|
28
|
+
streaming: bool,
|
|
29
|
+
) -> Any: ...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class HubDatasetRepository:
|
|
34
|
+
"""Pinned view of one Hugging Face Dataset repository."""
|
|
35
|
+
|
|
36
|
+
repo_id: str
|
|
37
|
+
requested_revision: str | None
|
|
38
|
+
resolved_revision: str
|
|
39
|
+
token: str | None = field(default=None, repr=False, compare=False)
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def resolve(
|
|
43
|
+
cls,
|
|
44
|
+
repo_id: str,
|
|
45
|
+
revision: str | None = None,
|
|
46
|
+
*,
|
|
47
|
+
token: str | None = None,
|
|
48
|
+
) -> HubDatasetRepository:
|
|
49
|
+
try:
|
|
50
|
+
info = HfApi(token=token).dataset_info(repo_id, revision=revision)
|
|
51
|
+
except Exception as exc:
|
|
52
|
+
raise DatasetLoadError(
|
|
53
|
+
f"failed to resolve Hugging Face dataset {repo_id!r}"
|
|
54
|
+
) from exc
|
|
55
|
+
if not info.sha:
|
|
56
|
+
raise DatasetLoadError(
|
|
57
|
+
f"Hugging Face dataset {repo_id!r} did not return a commit SHA"
|
|
58
|
+
)
|
|
59
|
+
return cls(
|
|
60
|
+
repo_id=repo_id,
|
|
61
|
+
requested_revision=revision,
|
|
62
|
+
resolved_revision=info.sha,
|
|
63
|
+
token=token,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def read_text(self, path: str) -> str:
|
|
67
|
+
try:
|
|
68
|
+
local_path = hf_hub_download(
|
|
69
|
+
repo_id=self.repo_id,
|
|
70
|
+
filename=path,
|
|
71
|
+
repo_type="dataset",
|
|
72
|
+
revision=self.resolved_revision,
|
|
73
|
+
token=self.token,
|
|
74
|
+
)
|
|
75
|
+
return Path(local_path).read_text(encoding="utf-8")
|
|
76
|
+
except Exception as exc:
|
|
77
|
+
raise DatasetLoadError(
|
|
78
|
+
f"failed to read {path!r} from dataset {self.repo_id!r} "
|
|
79
|
+
f"at revision {self.resolved_revision}"
|
|
80
|
+
) from exc
|
|
81
|
+
|
|
82
|
+
def load(
|
|
83
|
+
self,
|
|
84
|
+
config_name: str,
|
|
85
|
+
split: str,
|
|
86
|
+
*,
|
|
87
|
+
streaming: bool,
|
|
88
|
+
) -> Any:
|
|
89
|
+
try:
|
|
90
|
+
return load_dataset(
|
|
91
|
+
self.repo_id,
|
|
92
|
+
name=config_name,
|
|
93
|
+
split=split,
|
|
94
|
+
revision=self.resolved_revision,
|
|
95
|
+
streaming=streaming,
|
|
96
|
+
token=self.token,
|
|
97
|
+
trust_remote_code=False,
|
|
98
|
+
)
|
|
99
|
+
except Exception as exc:
|
|
100
|
+
raise DatasetLoadError(
|
|
101
|
+
f"failed to load config {config_name!r}, split {split!r} from "
|
|
102
|
+
f"dataset {self.repo_id!r} at revision {self.resolved_revision}"
|
|
103
|
+
) from exc
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Main Dataset aggregate."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from sci_modeling_bench.dataset._hub import DatasetRepository, HubDatasetRepository
|
|
9
|
+
from sci_modeling_bench.dataset.knowledge import Knowledge, KnowledgeResource
|
|
10
|
+
from sci_modeling_bench.dataset.manifest import (
|
|
11
|
+
DatasetCollectionManifest,
|
|
12
|
+
DatasetManifest,
|
|
13
|
+
DatasetSplit,
|
|
14
|
+
)
|
|
15
|
+
from sci_modeling_bench.dataset.metadata import DatasetMetadata
|
|
16
|
+
from sci_modeling_bench.dataset.schema import DatasetSchema
|
|
17
|
+
from sci_modeling_bench.dataset.source import HubDatasetSource
|
|
18
|
+
from sci_modeling_bench.dataset.validation import (
|
|
19
|
+
DatasetValidator,
|
|
20
|
+
ValidationReport,
|
|
21
|
+
Violation,
|
|
22
|
+
validate_fields,
|
|
23
|
+
)
|
|
24
|
+
from sci_modeling_bench.exceptions import SchemaMismatchError
|
|
25
|
+
|
|
26
|
+
COLLECTION_MANIFEST_FILENAME = "scimodelingbench.json"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Dataset:
|
|
30
|
+
"""Pinned scientific observations with semantics and validation."""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
manifest: DatasetManifest,
|
|
35
|
+
repository: DatasetRepository,
|
|
36
|
+
*,
|
|
37
|
+
config_name: str,
|
|
38
|
+
validator: DatasetValidator | None = None,
|
|
39
|
+
) -> None:
|
|
40
|
+
self._manifest = manifest
|
|
41
|
+
self._repository = repository
|
|
42
|
+
self._config_name = config_name
|
|
43
|
+
self._validator = validator or DatasetValidator()
|
|
44
|
+
self._loaded: dict[str, Any] = {}
|
|
45
|
+
self._knowledge = Knowledge(
|
|
46
|
+
{
|
|
47
|
+
name: KnowledgeResource(
|
|
48
|
+
title=spec.title,
|
|
49
|
+
description=spec.description,
|
|
50
|
+
path=spec.path,
|
|
51
|
+
media_type=spec.media_type,
|
|
52
|
+
_reader=repository.read_text,
|
|
53
|
+
)
|
|
54
|
+
for name, spec in manifest.knowledge.items()
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_hub(
|
|
60
|
+
cls,
|
|
61
|
+
repo_id: str,
|
|
62
|
+
config_name: str | None = None,
|
|
63
|
+
revision: str | None = None,
|
|
64
|
+
*,
|
|
65
|
+
token: str | None = None,
|
|
66
|
+
validator: DatasetValidator | None = None,
|
|
67
|
+
) -> Dataset:
|
|
68
|
+
"""Load one config from a pinned Hugging Face Dataset repository."""
|
|
69
|
+
|
|
70
|
+
return cls.from_source(
|
|
71
|
+
HubDatasetSource(
|
|
72
|
+
repo_id=repo_id,
|
|
73
|
+
config_name=config_name,
|
|
74
|
+
revision=revision,
|
|
75
|
+
),
|
|
76
|
+
token=token,
|
|
77
|
+
validator=validator,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def from_source(
|
|
82
|
+
cls,
|
|
83
|
+
source: HubDatasetSource,
|
|
84
|
+
*,
|
|
85
|
+
token: str | None = None,
|
|
86
|
+
validator: DatasetValidator | None = None,
|
|
87
|
+
) -> Dataset:
|
|
88
|
+
"""Load a Dataset from an explicit, reusable Hub source reference."""
|
|
89
|
+
|
|
90
|
+
repository = HubDatasetRepository.resolve(
|
|
91
|
+
source.repo_id,
|
|
92
|
+
source.revision,
|
|
93
|
+
token=token,
|
|
94
|
+
)
|
|
95
|
+
collection = DatasetCollectionManifest.from_json(
|
|
96
|
+
repository.read_text(COLLECTION_MANIFEST_FILENAME)
|
|
97
|
+
)
|
|
98
|
+
config_name, config = collection.select(source.config_name)
|
|
99
|
+
manifest = DatasetManifest.from_json(
|
|
100
|
+
repository.read_text(config.manifest)
|
|
101
|
+
)
|
|
102
|
+
return cls(
|
|
103
|
+
manifest,
|
|
104
|
+
repository,
|
|
105
|
+
config_name=config_name,
|
|
106
|
+
validator=validator,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def metadata(self) -> DatasetMetadata:
|
|
111
|
+
return self._manifest.metadata
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def schema(self) -> DatasetSchema:
|
|
115
|
+
return self._manifest.dataset_schema
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def knowledge(self) -> Knowledge:
|
|
119
|
+
return self._knowledge
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def config_name(self) -> str:
|
|
123
|
+
return self._config_name
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def default_split(self) -> str:
|
|
127
|
+
return self._manifest.default_split
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def splits(self) -> tuple[DatasetSplit, ...]:
|
|
131
|
+
return self._manifest.splits
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def available_splits(self) -> tuple[str, ...]:
|
|
135
|
+
return self._manifest.split_names
|
|
136
|
+
|
|
137
|
+
def split(self, name: str | None = None) -> DatasetSplit:
|
|
138
|
+
"""Return metadata for a declared split."""
|
|
139
|
+
|
|
140
|
+
selected = name or self.default_split
|
|
141
|
+
for split in self.splits:
|
|
142
|
+
if split.name == selected:
|
|
143
|
+
return split
|
|
144
|
+
available = ", ".join(self.available_splits)
|
|
145
|
+
raise ValueError(
|
|
146
|
+
f"split {selected!r} is not declared for config "
|
|
147
|
+
f"{self.config_name!r}; available: {available}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def repo_id(self) -> str:
|
|
152
|
+
return self._repository.repo_id
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def resolved_revision(self) -> str:
|
|
156
|
+
return self._repository.resolved_revision
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def features(self) -> Any:
|
|
160
|
+
"""Return physical Hugging Face features for the default split."""
|
|
161
|
+
|
|
162
|
+
return self.load().features
|
|
163
|
+
|
|
164
|
+
def load(
|
|
165
|
+
self,
|
|
166
|
+
split: str | None = None,
|
|
167
|
+
*,
|
|
168
|
+
streaming: bool = False,
|
|
169
|
+
) -> Any:
|
|
170
|
+
"""Load a standard Hugging Face Dataset or IterableDataset."""
|
|
171
|
+
|
|
172
|
+
selected_split = split or self._manifest.default_split
|
|
173
|
+
split_metadata = self.split(selected_split)
|
|
174
|
+
if not streaming and selected_split in self._loaded:
|
|
175
|
+
return self._loaded[selected_split]
|
|
176
|
+
|
|
177
|
+
data = self._repository.load(
|
|
178
|
+
self.config_name,
|
|
179
|
+
selected_split,
|
|
180
|
+
streaming=streaming,
|
|
181
|
+
)
|
|
182
|
+
self._ensure_declared_features(data)
|
|
183
|
+
if not streaming and split_metadata.num_rows is not None:
|
|
184
|
+
actual_rows = len(data)
|
|
185
|
+
if actual_rows != split_metadata.num_rows:
|
|
186
|
+
raise SchemaMismatchError(
|
|
187
|
+
f"config {self.config_name!r}, split {selected_split!r} declares "
|
|
188
|
+
f"{split_metadata.num_rows} rows but loaded {actual_rows}"
|
|
189
|
+
)
|
|
190
|
+
if not streaming:
|
|
191
|
+
self._loaded[selected_split] = data
|
|
192
|
+
return data
|
|
193
|
+
|
|
194
|
+
def validate_inputs(
|
|
195
|
+
self,
|
|
196
|
+
inputs: Mapping[str, Any],
|
|
197
|
+
context: Mapping[str, Any] | None = None,
|
|
198
|
+
) -> ValidationReport:
|
|
199
|
+
mapping_report = _require_mapping(inputs, "inputs")
|
|
200
|
+
if not mapping_report.valid:
|
|
201
|
+
return mapping_report
|
|
202
|
+
context_values = {} if context is None else context
|
|
203
|
+
context_report = _require_mapping(context_values, "context")
|
|
204
|
+
if not context_report.valid:
|
|
205
|
+
return context_report
|
|
206
|
+
|
|
207
|
+
features = self.features
|
|
208
|
+
return ValidationReport.combine(
|
|
209
|
+
validate_fields(inputs, self.schema.inputs, features=features),
|
|
210
|
+
validate_fields(context_values, self.schema.context, features=features),
|
|
211
|
+
self._validator.validate_inputs(inputs, context),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def validate_targets(
|
|
215
|
+
self,
|
|
216
|
+
targets: Mapping[str, Any],
|
|
217
|
+
inputs: Mapping[str, Any] | None = None,
|
|
218
|
+
context: Mapping[str, Any] | None = None,
|
|
219
|
+
) -> ValidationReport:
|
|
220
|
+
mapping_report = _require_mapping(targets, "targets")
|
|
221
|
+
if not mapping_report.valid:
|
|
222
|
+
return mapping_report
|
|
223
|
+
context_values = {} if context is None else context
|
|
224
|
+
context_report = _require_mapping(context_values, "context")
|
|
225
|
+
if not context_report.valid:
|
|
226
|
+
return context_report
|
|
227
|
+
|
|
228
|
+
features = self.features
|
|
229
|
+
return ValidationReport.combine(
|
|
230
|
+
validate_fields(targets, self.schema.targets, features=features),
|
|
231
|
+
validate_fields(context_values, self.schema.context, features=features),
|
|
232
|
+
self._validator.validate_targets(targets, inputs, context),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def validate_observation(
|
|
236
|
+
self,
|
|
237
|
+
observation: Mapping[str, Any],
|
|
238
|
+
) -> ValidationReport:
|
|
239
|
+
mapping_report = _require_mapping(observation, "observation")
|
|
240
|
+
if not mapping_report.valid:
|
|
241
|
+
return mapping_report
|
|
242
|
+
|
|
243
|
+
features = self.features
|
|
244
|
+
generic = validate_fields(
|
|
245
|
+
observation,
|
|
246
|
+
self.schema.fields,
|
|
247
|
+
features=features,
|
|
248
|
+
reject_unknown=False,
|
|
249
|
+
)
|
|
250
|
+
return ValidationReport.combine(
|
|
251
|
+
generic,
|
|
252
|
+
self._validator.validate_observation(observation),
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
def validate_dataset(self, data: Any | None = None) -> ValidationReport:
|
|
256
|
+
"""Validate all observations plus optional dataset-level custom checks."""
|
|
257
|
+
|
|
258
|
+
selected_data = data if data is not None else self.load()
|
|
259
|
+
self._ensure_declared_features(selected_data)
|
|
260
|
+
violations: list[Violation] = []
|
|
261
|
+
for row_index, observation in enumerate(selected_data):
|
|
262
|
+
report = self.validate_observation(observation)
|
|
263
|
+
if report.violations:
|
|
264
|
+
violations.extend(report.at_row(row_index).violations)
|
|
265
|
+
violations.extend(
|
|
266
|
+
self._validator.validate_dataset(selected_data).violations
|
|
267
|
+
)
|
|
268
|
+
return ValidationReport(violations=tuple(violations))
|
|
269
|
+
|
|
270
|
+
def _ensure_declared_features(self, data: Any) -> None:
|
|
271
|
+
features = getattr(data, "features", None)
|
|
272
|
+
if features is None:
|
|
273
|
+
return
|
|
274
|
+
missing = sorted(set(self.schema.field_names) - set(features))
|
|
275
|
+
if missing:
|
|
276
|
+
raise SchemaMismatchError(
|
|
277
|
+
f"dataset {self.metadata.dataset_id!r} is missing declared columns: {missing}"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _require_mapping(value: Any, label: str) -> ValidationReport:
|
|
282
|
+
if isinstance(value, Mapping):
|
|
283
|
+
return ValidationReport()
|
|
284
|
+
return ValidationReport(
|
|
285
|
+
violations=(
|
|
286
|
+
Violation(
|
|
287
|
+
code="invalid_mapping",
|
|
288
|
+
message=f"{label} must be a mapping keyed by semantic column name",
|
|
289
|
+
),
|
|
290
|
+
)
|
|
291
|
+
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Lazy, read-only domain knowledge resources bound to a dataset revision."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Iterator, Mapping
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class KnowledgeResource:
|
|
12
|
+
"""Metadata and lazy text access for one knowledge resource."""
|
|
13
|
+
|
|
14
|
+
title: str
|
|
15
|
+
description: str | None
|
|
16
|
+
path: str
|
|
17
|
+
media_type: str
|
|
18
|
+
_reader: Callable[[str], str] = field(repr=False, compare=False)
|
|
19
|
+
|
|
20
|
+
def read_text(self) -> str:
|
|
21
|
+
"""Read this resource from the dataset's resolved repository revision."""
|
|
22
|
+
|
|
23
|
+
return self._reader(self.path)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Knowledge(Mapping[str, KnowledgeResource]):
|
|
27
|
+
"""Read-only mapping of stable names to lazily loaded text resources."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, resources: Mapping[str, KnowledgeResource] | None = None) -> None:
|
|
30
|
+
self._resources = MappingProxyType(dict(resources or {}))
|
|
31
|
+
|
|
32
|
+
def __getitem__(self, name: str) -> KnowledgeResource:
|
|
33
|
+
return self._resources[name]
|
|
34
|
+
|
|
35
|
+
def __iter__(self) -> Iterator[str]:
|
|
36
|
+
return iter(self._resources)
|
|
37
|
+
|
|
38
|
+
def __len__(self) -> int:
|
|
39
|
+
return len(self._resources)
|
|
40
|
+
|
|
41
|
+
def read_text(self, name: str) -> str:
|
|
42
|
+
"""Read a named resource without eagerly loading any other resource."""
|
|
43
|
+
|
|
44
|
+
return self[name].read_text()
|