diffract-core 0.2.1__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.
- diffract/__init__.py +52 -0
- diffract/configs/fast_speed_without_disk.ini +48 -0
- diffract/configs/hybrid.ini +74 -0
- diffract/configs/sqlite.ini +62 -0
- diffract/containers.py +405 -0
- diffract/core/__init__.py +23 -0
- diffract/core/cache/__init__.py +24 -0
- diffract/core/cache/containers.py +93 -0
- diffract/core/cache/interface.py +117 -0
- diffract/core/cache/redis_manager.py +464 -0
- diffract/core/cache/simple_manager.py +478 -0
- diffract/core/compute/__init__.py +46 -0
- diffract/core/compute/config.py +48 -0
- diffract/core/compute/containers.py +81 -0
- diffract/core/compute/decorator.py +125 -0
- diffract/core/compute/exceptions.py +31 -0
- diffract/core/compute/execution/__init__.py +14 -0
- diffract/core/compute/execution/_types.py +70 -0
- diffract/core/compute/execution/aggregation.py +74 -0
- diffract/core/compute/execution/aggregation_runner.py +362 -0
- diffract/core/compute/execution/enums.py +38 -0
- diffract/core/compute/execution/executor.py +125 -0
- diffract/core/compute/execution/parameter_runner.py +125 -0
- diffract/core/compute/execution/restrictions.py +61 -0
- diffract/core/compute/execution/strategy.py +118 -0
- diffract/core/compute/execution/utils.py +112 -0
- diffract/core/compute/extensions/__init__.py +0 -0
- diffract/core/compute/extensions/power_law.py +1051 -0
- diffract/core/compute/extensions/rmt.py +150 -0
- diffract/core/compute/extensions/utils.py +36 -0
- diffract/core/compute/field_signature.py +183 -0
- diffract/core/compute/kernels/__init__.py +48 -0
- diffract/core/compute/kernels/alignment.py +106 -0
- diffract/core/compute/kernels/heavy_tailed.py +394 -0
- diffract/core/compute/kernels/marchenko_pastur.py +99 -0
- diffract/core/compute/kernels/mat_decomposition.py +101 -0
- diffract/core/compute/kernels/mat_properties.py +53 -0
- diffract/core/compute/kernels/model_quality.py +27 -0
- diffract/core/compute/kernels/norms.py +88 -0
- diffract/core/compute/kernels/ranks.py +35 -0
- diffract/core/compute/kernels/tracy_widom.py +36 -0
- diffract/core/compute/metadata.py +67 -0
- diffract/core/compute/registry.py +485 -0
- diffract/core/constants.py +147 -0
- diffract/core/data/__init__.py +32 -0
- diffract/core/data/interface.py +304 -0
- diffract/core/data/metadata/__init__.py +15 -0
- diffract/core/data/metadata/containers.py +60 -0
- diffract/core/data/metadata/interface.py +208 -0
- diffract/core/data/metadata/sqlite_index.py +604 -0
- diffract/core/data/nn/__init__.py +43 -0
- diffract/core/data/nn/aggregates/__init__.py +35 -0
- diffract/core/data/nn/aggregates/metadata.py +96 -0
- diffract/core/data/nn/aggregates/proxy.py +57 -0
- diffract/core/data/nn/aggregates/repository.py +87 -0
- diffract/core/data/nn/aggregates/view.py +307 -0
- diffract/core/data/nn/containers.py +86 -0
- diffract/core/data/nn/extractors/__init__.py +49 -0
- diffract/core/data/nn/extractors/base.py +300 -0
- diffract/core/data/nn/extractors/factory.py +234 -0
- diffract/core/data/nn/extractors/flax.py +112 -0
- diffract/core/data/nn/extractors/handlers/__init__.py +39 -0
- diffract/core/data/nn/extractors/handlers/base.py +110 -0
- diffract/core/data/nn/extractors/handlers/flax_handlers.py +71 -0
- diffract/core/data/nn/extractors/handlers/numpy_handlers.py +63 -0
- diffract/core/data/nn/extractors/handlers/onnx_handlers.py +67 -0
- diffract/core/data/nn/extractors/handlers/tensorflow_handlers.py +82 -0
- diffract/core/data/nn/extractors/handlers/torch_handlers.py +124 -0
- diffract/core/data/nn/extractors/interface.py +56 -0
- diffract/core/data/nn/extractors/numpy.py +94 -0
- diffract/core/data/nn/extractors/onnx.py +108 -0
- diffract/core/data/nn/extractors/tensorflow.py +99 -0
- diffract/core/data/nn/extractors/torch.py +204 -0
- diffract/core/data/nn/params/__init__.py +27 -0
- diffract/core/data/nn/params/interface.py +402 -0
- diffract/core/data/nn/params/metadata.py +110 -0
- diffract/core/data/nn/params/proxy.py +93 -0
- diffract/core/data/nn/params/repository.py +45 -0
- diffract/core/data/nn/params/schema.py +82 -0
- diffract/core/data/nn/params/view.py +393 -0
- diffract/core/data/proxy.py +246 -0
- diffract/core/data/repository.py +227 -0
- diffract/core/data/utils.py +33 -0
- diffract/core/data/view.py +389 -0
- diffract/core/export/__init__.py +27 -0
- diffract/core/export/containers.py +47 -0
- diffract/core/export/exporters.py +191 -0
- diffract/core/export/formatters/__init__.py +23 -0
- diffract/core/export/formatters/base.py +68 -0
- diffract/core/export/formatters/dict_formatter.py +38 -0
- diffract/core/export/formatters/json_formatter.py +58 -0
- diffract/core/export/formatters/list_formatter.py +41 -0
- diffract/core/export/formatters/pandas_formatter.py +86 -0
- diffract/core/export/formatters/polars_formatter.py +86 -0
- diffract/core/export/formatters/registry.py +84 -0
- diffract/core/export/interface.py +105 -0
- diffract/core/parallel/__init__.py +21 -0
- diffract/core/parallel/containers.py +57 -0
- diffract/core/parallel/execution.py +91 -0
- diffract/core/parallel/runtime.py +91 -0
- diffract/core/storage/__init__.py +35 -0
- diffract/core/storage/base_manager.py +330 -0
- diffract/core/storage/containers.py +122 -0
- diffract/core/storage/hdf5_manager.py +856 -0
- diffract/core/storage/hybrid_manager.py +218 -0
- diffract/core/storage/interface.py +222 -0
- diffract/core/storage/metadata.py +89 -0
- diffract/core/storage/ram_manager.py +159 -0
- diffract/core/storage/sqlite_manager.py +1062 -0
- diffract/core/storage/zarr_manager.py +992 -0
- diffract/core/utils/__init__.py +45 -0
- diffract/core/utils/build.py +46 -0
- diffract/core/utils/exceptions.py +11 -0
- diffract/core/utils/hashing.py +90 -0
- diffract/core/utils/imports.py +292 -0
- diffract/core/utils/math.py +15 -0
- diffract/py.typed +0 -0
- diffract/session/__init__.py +24 -0
- diffract/session/errors.py +17 -0
- diffract/session/field_cache.py +216 -0
- diffract/session/namespaces/__init__.py +15 -0
- diffract/session/namespaces/compute/__init__.py +219 -0
- diffract/session/namespaces/models/__init__.py +282 -0
- diffract/session/namespaces/models/parameters/__init__.py +133 -0
- diffract/session/namespaces/models/parameters/meta_patcher.py +167 -0
- diffract/session/namespaces/results/__init__.py +378 -0
- diffract/session/namespaces/results/eraser.py +129 -0
- diffract/session/namespaces/results/injester.py +249 -0
- diffract/session/namespaces/utils/__init__.py +141 -0
- diffract/session/namespaces/utils/merger.py +295 -0
- diffract/session/namespaces/viz/__init__.py +91 -0
- diffract/session/namespaces/viz/_utils.py +14 -0
- diffract/session/namespaces/viz/box.py +207 -0
- diffract/session/namespaces/viz/grid.py +160 -0
- diffract/session/namespaces/viz/heatmap.py +164 -0
- diffract/session/namespaces/viz/scatter.py +202 -0
- diffract/session/namespaces/viz/sparkline.py +270 -0
- diffract/session/namespaces/viz/violin.py +212 -0
- diffract/session/session.py +260 -0
- diffract/session/utils.py +81 -0
- diffract/viz/__init__.py +42 -0
- diffract/viz/data/__init__.py +34 -0
- diffract/viz/data/detection.py +57 -0
- diffract/viz/data/extraction.py +117 -0
- diffract/viz/data/filtering.py +57 -0
- diffract/viz/data/ordering.py +129 -0
- diffract/viz/data/provider.py +99 -0
- diffract/viz/data/types.py +98 -0
- diffract/viz/plots/__init__.py +37 -0
- diffract/viz/plots/base/__init__.py +20 -0
- diffract/viz/plots/base/axis.py +219 -0
- diffract/viz/plots/base/coloraxis.py +133 -0
- diffract/viz/plots/base/configurator.py +18 -0
- diffract/viz/plots/base/jitter.py +368 -0
- diffract/viz/plots/base/line.py +197 -0
- diffract/viz/plots/base/marker.py +278 -0
- diffract/viz/plots/base/overlay.py +14 -0
- diffract/viz/plots/base/plot.py +110 -0
- diffract/viz/plots/base/update.py +50 -0
- diffract/viz/plots/boxplot.py +283 -0
- diffract/viz/plots/cluster.py +374 -0
- diffract/viz/plots/heatmap.py +168 -0
- diffract/viz/plots/scatter.py +233 -0
- diffract/viz/plots/sparkline.py +405 -0
- diffract/viz/plots/subplots/__init__.py +32 -0
- diffract/viz/plots/subplots/coloraxis.py +339 -0
- diffract/viz/plots/subplots/factory.py +713 -0
- diffract/viz/plots/subplots/grid.py +214 -0
- diffract/viz/plots/subplots/layout.py +370 -0
- diffract/viz/plots/subplots/spec.py +39 -0
- diffract/viz/plots/violin.py +298 -0
- diffract/viz/renderer.py +513 -0
- diffract/viz/styling/__init__.py +78 -0
- diffract/viz/styling/palettes/__init__.py +20 -0
- diffract/viz/styling/palettes/bundle.py +16 -0
- diffract/viz/styling/palettes/color.py +83 -0
- diffract/viz/styling/palettes/dashes.py +27 -0
- diffract/viz/styling/palettes/symbols.py +42 -0
- diffract/viz/styling/resolvers/__init__.py +11 -0
- diffract/viz/styling/resolvers/categorical.py +68 -0
- diffract/viz/styling/resolvers/color.py +77 -0
- diffract/viz/styling/resolvers/numeric.py +108 -0
- diffract/viz/styling/sources.py +27 -0
- diffract/viz/styling/theme/__init__.py +29 -0
- diffract/viz/styling/theme/applier.py +114 -0
- diffract/viz/styling/theme/components.py +66 -0
- diffract/viz/styling/theme/presets.py +58 -0
- diffract/viz/styling/theme/theme.py +36 -0
- diffract_core-0.2.1.dist-info/METADATA +530 -0
- diffract_core-0.2.1.dist-info/RECORD +193 -0
- diffract_core-0.2.1.dist-info/WHEEL +4 -0
- diffract_core-0.2.1.dist-info/licenses/LICENSE +201 -0
- diffract_core-0.2.1.dist-info/licenses/NOTICE +2 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
"""Generic protocols for data abstractions in diffract.
|
|
2
|
+
|
|
3
|
+
This module defines the core protocols for domain-agnostic data management.
|
|
4
|
+
It provides framework-agnostic contracts for:
|
|
5
|
+
|
|
6
|
+
1. IMetadata: Serializable metadata with unique identifier
|
|
7
|
+
2. IDataProxy: Lazy-loading proxy for storage-backed data
|
|
8
|
+
3. IDataView: Batch operations on filtered collections
|
|
9
|
+
4. IDataRepository: Ownership of storage/cache and membership management
|
|
10
|
+
|
|
11
|
+
These protocols are implemented by generic classes that can be specialized
|
|
12
|
+
for different data types (parameters, relations, etc.).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import (
|
|
18
|
+
TYPE_CHECKING,
|
|
19
|
+
Any,
|
|
20
|
+
Protocol,
|
|
21
|
+
Self,
|
|
22
|
+
TypeVar,
|
|
23
|
+
overload,
|
|
24
|
+
runtime_checkable,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
import types
|
|
29
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
30
|
+
|
|
31
|
+
from diffract.core.cache.interface import ICacheManager
|
|
32
|
+
from diffract.core.data.metadata.interface import IMetadataIndex
|
|
33
|
+
from diffract.core.parallel import ParallelContext
|
|
34
|
+
from diffract.core.storage.interface import IStorageManager
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@runtime_checkable
|
|
38
|
+
class IMetadata(Protocol):
|
|
39
|
+
"""Protocol for metadata objects with serialization support.
|
|
40
|
+
|
|
41
|
+
Metadata objects must have a unique identifier and support
|
|
42
|
+
dictionary serialization for storage persistence.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def uid(self) -> str:
|
|
47
|
+
"""Unique identifier for this metadata object."""
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
def to_dict(self) -> dict[str, Any]:
|
|
51
|
+
"""Serialize metadata to dictionary for storage.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dictionary representation suitable for storage.
|
|
55
|
+
"""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def from_dict(cls, data: dict[str, Any]) -> Self:
|
|
60
|
+
"""Deserialize metadata from dictionary.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
data: Dictionary with metadata fields.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Metadata instance.
|
|
67
|
+
"""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
TMetadata = TypeVar("TMetadata", bound=IMetadata)
|
|
72
|
+
TProxy = TypeVar("TProxy", bound="IDataProxy")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@runtime_checkable
|
|
76
|
+
class IDataProxy(Protocol[TMetadata]):
|
|
77
|
+
"""Protocol for a proxy object representing a single data entity.
|
|
78
|
+
|
|
79
|
+
This protocol describes how the system interacts with a single
|
|
80
|
+
data object (conceptually: a single "row" in storage). The proxy
|
|
81
|
+
is a *handle* for interacting with storage-backed data; it should not
|
|
82
|
+
be interpreted as an owner of storage/cache managers.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def meta(self) -> TMetadata:
|
|
87
|
+
"""Metadata for this entity."""
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
def set_field(self, name: str, value: Any) -> None:
|
|
91
|
+
"""Store a named field for this entity."""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
def get_field(
|
|
95
|
+
self, name: str, default: Any | None = None, auto_prefetch: bool = True
|
|
96
|
+
) -> Any:
|
|
97
|
+
"""Retrieve a named field for this entity (cache-first)."""
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
def has_field(self, name: str) -> bool:
|
|
101
|
+
"""Return True if the field exists (storage or cache)."""
|
|
102
|
+
...
|
|
103
|
+
|
|
104
|
+
def get_field_metadata(self, name: str) -> dict[str, Any] | None:
|
|
105
|
+
"""Return storage-level metadata for the given field if available."""
|
|
106
|
+
...
|
|
107
|
+
|
|
108
|
+
def is_field_prefetched(self, name: str) -> bool:
|
|
109
|
+
"""Return True if the field is cached for fast access."""
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
def prefetch_field(self, name: str, value: Any) -> None:
|
|
113
|
+
"""Cache a field value for fast access."""
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
def try_prefetch_field(self, name: str) -> bool:
|
|
117
|
+
"""Attempt to prefetch a field into cache; returns False if missing."""
|
|
118
|
+
...
|
|
119
|
+
|
|
120
|
+
def erase_field(self, name: str) -> None:
|
|
121
|
+
"""Remove a field from storage and cache."""
|
|
122
|
+
...
|
|
123
|
+
|
|
124
|
+
def list_fields(self) -> list[str]:
|
|
125
|
+
"""List all field names for this entity."""
|
|
126
|
+
...
|
|
127
|
+
|
|
128
|
+
def __hash__(self) -> int:
|
|
129
|
+
"""Hash based on entity identity."""
|
|
130
|
+
...
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@runtime_checkable
|
|
134
|
+
class IDataRepository(Protocol[TMetadata, TProxy]):
|
|
135
|
+
"""Protocol for a repository owning storage and cache infrastructure.
|
|
136
|
+
|
|
137
|
+
Defines the interface for repositories that exclusively own and manage
|
|
138
|
+
the IStorageManager and ICacheManager instances. The repository is the
|
|
139
|
+
single source of truth for persistent data and acts as the exclusive
|
|
140
|
+
owner of the underlying storage and caching infrastructure.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def storage_manager(self) -> IStorageManager:
|
|
145
|
+
"""Storage manager owned by this repository."""
|
|
146
|
+
...
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def metadata_index(self) -> IMetadataIndex:
|
|
150
|
+
"""Metadata index owned by this repository."""
|
|
151
|
+
...
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def cache_manager(self) -> ICacheManager | None:
|
|
155
|
+
"""Cache manager owned by this repository."""
|
|
156
|
+
...
|
|
157
|
+
|
|
158
|
+
def __enter__(self) -> Self:
|
|
159
|
+
"""Enter a batch write context."""
|
|
160
|
+
...
|
|
161
|
+
|
|
162
|
+
def __exit__(
|
|
163
|
+
self,
|
|
164
|
+
exc_type: type[BaseException] | None,
|
|
165
|
+
exc_val: BaseException | None,
|
|
166
|
+
exc_tb: types.TracebackType | None,
|
|
167
|
+
) -> None:
|
|
168
|
+
"""Exit the batch write context and flush pending operations."""
|
|
169
|
+
...
|
|
170
|
+
|
|
171
|
+
def __len__(self) -> int:
|
|
172
|
+
"""Return the total number of entities in the repository."""
|
|
173
|
+
...
|
|
174
|
+
|
|
175
|
+
def __iter__(self) -> Iterator[TProxy]:
|
|
176
|
+
"""Iterate over entities in the repository."""
|
|
177
|
+
...
|
|
178
|
+
|
|
179
|
+
def __getitem__(self, index: int) -> TProxy:
|
|
180
|
+
"""Get an entity by index."""
|
|
181
|
+
...
|
|
182
|
+
|
|
183
|
+
def get_proxy(self, uid: str) -> TProxy:
|
|
184
|
+
"""Return a proxy for an entity uid currently present in the repository."""
|
|
185
|
+
...
|
|
186
|
+
|
|
187
|
+
def append(self, proxy: TProxy) -> None:
|
|
188
|
+
"""Add a single entity to the repository (mutates membership)."""
|
|
189
|
+
...
|
|
190
|
+
|
|
191
|
+
def extend(self, proxies: Iterable[TProxy]) -> None:
|
|
192
|
+
"""Add multiple entities to the repository (mutates membership)."""
|
|
193
|
+
...
|
|
194
|
+
|
|
195
|
+
def remove_by_uid(self, uid: str) -> None:
|
|
196
|
+
"""Remove an entity from the repository by UID (mutates membership)."""
|
|
197
|
+
...
|
|
198
|
+
|
|
199
|
+
def clear(self, erase: bool = False) -> None:
|
|
200
|
+
"""Clear repository membership.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
erase: If True, also erase data from storage and cache.
|
|
204
|
+
"""
|
|
205
|
+
...
|
|
206
|
+
|
|
207
|
+
def create_view(self) -> IDataView[TMetadata, TProxy]:
|
|
208
|
+
"""Create a view for working with a subset of entities."""
|
|
209
|
+
...
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
type EntityUID = str
|
|
213
|
+
type EntityIndex = int
|
|
214
|
+
type FieldName = str
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@runtime_checkable
|
|
218
|
+
class IDataView(Protocol[TMetadata, TProxy]):
|
|
219
|
+
"""Protocol for a view with filtering and data access.
|
|
220
|
+
|
|
221
|
+
Defines the interface for views that provide efficient batch
|
|
222
|
+
operations and data access. Views support filtering and provide
|
|
223
|
+
convenient access to data through proxies.
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
def __len__(self) -> int:
|
|
227
|
+
"""Return the number of entities in this view."""
|
|
228
|
+
...
|
|
229
|
+
|
|
230
|
+
def __iter__(self) -> Iterator[TProxy]:
|
|
231
|
+
"""Iterate over entity proxies in view order."""
|
|
232
|
+
...
|
|
233
|
+
|
|
234
|
+
@overload
|
|
235
|
+
def __getitem__(self, index: EntityIndex) -> TProxy: ...
|
|
236
|
+
|
|
237
|
+
@overload
|
|
238
|
+
def __getitem__(self, index: slice) -> Self: ...
|
|
239
|
+
|
|
240
|
+
@overload
|
|
241
|
+
def __getitem__(self, uid: EntityUID) -> TProxy: ...
|
|
242
|
+
|
|
243
|
+
@overload
|
|
244
|
+
def __getitem__(self, indices: Sequence[EntityIndex]) -> Self: ...
|
|
245
|
+
|
|
246
|
+
@overload
|
|
247
|
+
def __getitem__(self, uids: Sequence[EntityUID]) -> Self: ...
|
|
248
|
+
|
|
249
|
+
def __getitem__(
|
|
250
|
+
self,
|
|
251
|
+
index: EntityIndex
|
|
252
|
+
| slice
|
|
253
|
+
| EntityUID
|
|
254
|
+
| Sequence[EntityIndex]
|
|
255
|
+
| Sequence[EntityUID],
|
|
256
|
+
) -> TProxy | Self:
|
|
257
|
+
"""Index into the view by position(s) or uid(s)."""
|
|
258
|
+
...
|
|
259
|
+
|
|
260
|
+
def list_uids(self) -> list[EntityUID]:
|
|
261
|
+
"""List entity unique identifiers in this view."""
|
|
262
|
+
...
|
|
263
|
+
|
|
264
|
+
def list_fields_by_uid(
|
|
265
|
+
self, *, parallel: ParallelContext | None = None
|
|
266
|
+
) -> dict[EntityUID, list[FieldName]]:
|
|
267
|
+
"""Return a uid -> list(fields) mapping for entities in this view."""
|
|
268
|
+
...
|
|
269
|
+
|
|
270
|
+
def prefetch_fields(
|
|
271
|
+
self,
|
|
272
|
+
*,
|
|
273
|
+
fields_by_uid: dict[EntityUID, list[FieldName]] | None = None,
|
|
274
|
+
fields: list[FieldName] | None = None,
|
|
275
|
+
verify_prefetch: bool = False,
|
|
276
|
+
parallel: ParallelContext | None = None,
|
|
277
|
+
) -> bool:
|
|
278
|
+
"""Prefetch specified fields for entities in this view into cache."""
|
|
279
|
+
...
|
|
280
|
+
|
|
281
|
+
def erase_fields(self, *fields: FieldName) -> None:
|
|
282
|
+
"""Remove specified fields from entities in this view."""
|
|
283
|
+
...
|
|
284
|
+
|
|
285
|
+
def erase_fields_with_regexp(self, *patterns: str) -> None:
|
|
286
|
+
"""Remove fields matching regex patterns from entities in this view."""
|
|
287
|
+
...
|
|
288
|
+
|
|
289
|
+
def filter_by_fields(
|
|
290
|
+
self,
|
|
291
|
+
*fields: FieldName,
|
|
292
|
+
inverse_mask: bool = False,
|
|
293
|
+
parallel: ParallelContext | None = None,
|
|
294
|
+
) -> Self:
|
|
295
|
+
"""Filter entities by field presence."""
|
|
296
|
+
...
|
|
297
|
+
|
|
298
|
+
def clear(self, erase: bool = False) -> None:
|
|
299
|
+
"""Clear the view.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
erase: If True, also erase data from storage and cache.
|
|
303
|
+
"""
|
|
304
|
+
...
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Metadata index module for structured metadata storage and querying.
|
|
2
|
+
|
|
3
|
+
This module provides a generic metadata indexing system that supports
|
|
4
|
+
SQL-based filtering and querying of entity metadata.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from .interface import IMetadataIndex
|
|
10
|
+
from .sqlite_index import SQLiteMetadataIndex
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"IMetadataIndex",
|
|
14
|
+
"SQLiteMetadataIndex",
|
|
15
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Dependency injection containers for metadata index management.
|
|
2
|
+
|
|
3
|
+
This module provides dependency injection configuration for metadata index
|
|
4
|
+
using the dependency-injector library.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from collections.abc import Generator
|
|
11
|
+
from contextlib import contextmanager
|
|
12
|
+
|
|
13
|
+
from dependency_injector import containers, providers
|
|
14
|
+
|
|
15
|
+
from diffract.core.utils.build import build_with_defaults
|
|
16
|
+
|
|
17
|
+
from .interface import IMetadataIndex
|
|
18
|
+
from .sqlite_index import SQLiteMetadataIndex
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MetadataContainer(containers.DeclarativeContainer):
|
|
24
|
+
"""Dependency injection container for metadata index components.
|
|
25
|
+
|
|
26
|
+
Provides configurable metadata index selection between different
|
|
27
|
+
backends. Currently supports SQLite for structured metadata storage.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
config = providers.Configuration()
|
|
31
|
+
|
|
32
|
+
sqlite_index = providers.Singleton(
|
|
33
|
+
build_with_defaults,
|
|
34
|
+
SQLiteMetadataIndex,
|
|
35
|
+
config.sqlite.as_(dict),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
metadata_index = providers.Selector(
|
|
39
|
+
config.backend,
|
|
40
|
+
sqlite=sqlite_index,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
metadata_index_resource = providers.Resource(
|
|
44
|
+
lambda index: _connect_wrapper(index),
|
|
45
|
+
metadata_index,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@contextmanager
|
|
50
|
+
def _connect_wrapper(index: IMetadataIndex) -> Generator[IMetadataIndex, None, None]:
|
|
51
|
+
logger.debug("Init resource: %s", index.__class__.__name__)
|
|
52
|
+
if hasattr(index, "connect"):
|
|
53
|
+
logger.debug("Init connection for: %s", index.__class__.__name__)
|
|
54
|
+
index.connect()
|
|
55
|
+
try:
|
|
56
|
+
yield index
|
|
57
|
+
finally:
|
|
58
|
+
if hasattr(index, "close"):
|
|
59
|
+
logger.debug("Close connection for: %s", index.__class__.__name__)
|
|
60
|
+
index.close()
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Metadata index interface and protocols.
|
|
2
|
+
|
|
3
|
+
This module defines the core protocol that all metadata index
|
|
4
|
+
implementations must follow. It provides a framework-agnostic
|
|
5
|
+
contract for structured metadata storage and querying.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import types
|
|
11
|
+
from typing import Any, Protocol, Self, runtime_checkable
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@runtime_checkable
|
|
15
|
+
class IMetadataIndex(Protocol):
|
|
16
|
+
"""Protocol defining the metadata index interface.
|
|
17
|
+
|
|
18
|
+
Provides a unified interface for structured metadata storage with
|
|
19
|
+
SQL-like querying capabilities. All implementations must support
|
|
20
|
+
table definition, CRUD operations, and flexible querying.
|
|
21
|
+
|
|
22
|
+
The index is organized as tables containing records identified by UID,
|
|
23
|
+
where each record has typed columns for efficient querying.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __enter__(self) -> Self:
|
|
27
|
+
"""Enter batch operation context for optimized writes."""
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
def __exit__(
|
|
31
|
+
self,
|
|
32
|
+
exc_type: type[BaseException] | None,
|
|
33
|
+
exc_val: BaseException | None,
|
|
34
|
+
exc_tb: types.TracebackType | None,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Exit batch context and commit pending operations."""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
def define_table(
|
|
40
|
+
self,
|
|
41
|
+
table: str,
|
|
42
|
+
columns: dict[str, type],
|
|
43
|
+
indexes: list[str] | None = None,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Define a table schema with typed columns.
|
|
46
|
+
|
|
47
|
+
Creates a table if it doesn't exist. Idempotent - calling multiple
|
|
48
|
+
times with the same schema has no effect.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
table: Table name.
|
|
52
|
+
columns: Mapping of column names to Python types (str, int, float, bool).
|
|
53
|
+
indexes: List of column names to create indexes on.
|
|
54
|
+
"""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
def insert(self, table: str, uid: str, **fields: Any) -> None:
|
|
58
|
+
"""Insert a new record into the table.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
table: Table name.
|
|
62
|
+
uid: Unique identifier for the record.
|
|
63
|
+
**fields: Column values to insert.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
ValueError: If record with uid already exists.
|
|
67
|
+
"""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
def update(self, table: str, uid: str, **fields: Any) -> None:
|
|
71
|
+
"""Update an existing record (partial update).
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
table: Table name.
|
|
75
|
+
uid: Unique identifier of the record to update.
|
|
76
|
+
**fields: Column values to update.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
KeyError: If record with uid doesn't exist.
|
|
80
|
+
"""
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
def upsert(self, table: str, uid: str, **fields: Any) -> None:
|
|
84
|
+
"""Insert or update a record.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
table: Table name.
|
|
88
|
+
uid: Unique identifier for the record.
|
|
89
|
+
**fields: Column values to insert or update.
|
|
90
|
+
"""
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
def get(self, table: str, uid: str) -> dict[str, Any] | None:
|
|
94
|
+
"""Get a single record by uid.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
table: Table name.
|
|
98
|
+
uid: Unique identifier of the record.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Dictionary of column values, or None if not found.
|
|
102
|
+
"""
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
def get_batch(self, table: str, uids: list[str]) -> list[dict[str, Any] | None]:
|
|
106
|
+
"""Get multiple records by uids.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
table: Table name.
|
|
110
|
+
uids: List of unique identifiers.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
List of dictionaries (or None for missing records), same order as uids.
|
|
114
|
+
"""
|
|
115
|
+
...
|
|
116
|
+
|
|
117
|
+
def query(
|
|
118
|
+
self,
|
|
119
|
+
table: str,
|
|
120
|
+
where: dict[str, Any] | None = None,
|
|
121
|
+
where_in: dict[str, list[Any]] | None = None,
|
|
122
|
+
where_like: dict[str, str] | None = None,
|
|
123
|
+
order_by: list[str] | None = None,
|
|
124
|
+
limit: int | None = None,
|
|
125
|
+
) -> list[str]:
|
|
126
|
+
"""Query UIDs matching criteria.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
table: Table name.
|
|
130
|
+
where: Exact match conditions {column: value}.
|
|
131
|
+
where_in: IN conditions {column: [values]}.
|
|
132
|
+
where_like: LIKE conditions {column: pattern}.
|
|
133
|
+
order_by: List of column names to sort by.
|
|
134
|
+
limit: Maximum number of results.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
List of UIDs matching the query.
|
|
138
|
+
"""
|
|
139
|
+
...
|
|
140
|
+
|
|
141
|
+
def delete(self, table: str, uid: str) -> None:
|
|
142
|
+
"""Delete a record by uid.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
table: Table name.
|
|
146
|
+
uid: Unique identifier of the record to delete.
|
|
147
|
+
"""
|
|
148
|
+
...
|
|
149
|
+
|
|
150
|
+
def delete_batch(self, table: str, uids: list[str]) -> None:
|
|
151
|
+
"""Delete multiple records by uids.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
table: Table name.
|
|
155
|
+
uids: List of unique identifiers to delete.
|
|
156
|
+
"""
|
|
157
|
+
...
|
|
158
|
+
|
|
159
|
+
def count(self, table: str, where: dict[str, Any] | None = None) -> int:
|
|
160
|
+
"""Count records in table.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
table: Table name.
|
|
164
|
+
where: Optional filter conditions.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Number of matching records.
|
|
168
|
+
"""
|
|
169
|
+
...
|
|
170
|
+
|
|
171
|
+
def distinct(self, table: str, column: str) -> list[Any]:
|
|
172
|
+
"""Get distinct values for a column.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
table: Table name.
|
|
176
|
+
column: Column name.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
List of distinct values.
|
|
180
|
+
"""
|
|
181
|
+
...
|
|
182
|
+
|
|
183
|
+
def list_uids(self, table: str) -> list[str]:
|
|
184
|
+
"""List all UIDs in a table.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
table: Table name.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
List of all UIDs.
|
|
191
|
+
"""
|
|
192
|
+
...
|
|
193
|
+
|
|
194
|
+
def clear(self, table: str | None = None) -> None:
|
|
195
|
+
"""Clear data from index.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
table: If provided, clear only this table. If None, clear all data.
|
|
199
|
+
"""
|
|
200
|
+
...
|
|
201
|
+
|
|
202
|
+
def connect(self) -> None:
|
|
203
|
+
"""Initialize connection to the index backend."""
|
|
204
|
+
...
|
|
205
|
+
|
|
206
|
+
def close(self) -> None:
|
|
207
|
+
"""Close connection to the index backend."""
|
|
208
|
+
...
|