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.
Files changed (193) hide show
  1. diffract/__init__.py +52 -0
  2. diffract/configs/fast_speed_without_disk.ini +48 -0
  3. diffract/configs/hybrid.ini +74 -0
  4. diffract/configs/sqlite.ini +62 -0
  5. diffract/containers.py +405 -0
  6. diffract/core/__init__.py +23 -0
  7. diffract/core/cache/__init__.py +24 -0
  8. diffract/core/cache/containers.py +93 -0
  9. diffract/core/cache/interface.py +117 -0
  10. diffract/core/cache/redis_manager.py +464 -0
  11. diffract/core/cache/simple_manager.py +478 -0
  12. diffract/core/compute/__init__.py +46 -0
  13. diffract/core/compute/config.py +48 -0
  14. diffract/core/compute/containers.py +81 -0
  15. diffract/core/compute/decorator.py +125 -0
  16. diffract/core/compute/exceptions.py +31 -0
  17. diffract/core/compute/execution/__init__.py +14 -0
  18. diffract/core/compute/execution/_types.py +70 -0
  19. diffract/core/compute/execution/aggregation.py +74 -0
  20. diffract/core/compute/execution/aggregation_runner.py +362 -0
  21. diffract/core/compute/execution/enums.py +38 -0
  22. diffract/core/compute/execution/executor.py +125 -0
  23. diffract/core/compute/execution/parameter_runner.py +125 -0
  24. diffract/core/compute/execution/restrictions.py +61 -0
  25. diffract/core/compute/execution/strategy.py +118 -0
  26. diffract/core/compute/execution/utils.py +112 -0
  27. diffract/core/compute/extensions/__init__.py +0 -0
  28. diffract/core/compute/extensions/power_law.py +1051 -0
  29. diffract/core/compute/extensions/rmt.py +150 -0
  30. diffract/core/compute/extensions/utils.py +36 -0
  31. diffract/core/compute/field_signature.py +183 -0
  32. diffract/core/compute/kernels/__init__.py +48 -0
  33. diffract/core/compute/kernels/alignment.py +106 -0
  34. diffract/core/compute/kernels/heavy_tailed.py +394 -0
  35. diffract/core/compute/kernels/marchenko_pastur.py +99 -0
  36. diffract/core/compute/kernels/mat_decomposition.py +101 -0
  37. diffract/core/compute/kernels/mat_properties.py +53 -0
  38. diffract/core/compute/kernels/model_quality.py +27 -0
  39. diffract/core/compute/kernels/norms.py +88 -0
  40. diffract/core/compute/kernels/ranks.py +35 -0
  41. diffract/core/compute/kernels/tracy_widom.py +36 -0
  42. diffract/core/compute/metadata.py +67 -0
  43. diffract/core/compute/registry.py +485 -0
  44. diffract/core/constants.py +147 -0
  45. diffract/core/data/__init__.py +32 -0
  46. diffract/core/data/interface.py +304 -0
  47. diffract/core/data/metadata/__init__.py +15 -0
  48. diffract/core/data/metadata/containers.py +60 -0
  49. diffract/core/data/metadata/interface.py +208 -0
  50. diffract/core/data/metadata/sqlite_index.py +604 -0
  51. diffract/core/data/nn/__init__.py +43 -0
  52. diffract/core/data/nn/aggregates/__init__.py +35 -0
  53. diffract/core/data/nn/aggregates/metadata.py +96 -0
  54. diffract/core/data/nn/aggregates/proxy.py +57 -0
  55. diffract/core/data/nn/aggregates/repository.py +87 -0
  56. diffract/core/data/nn/aggregates/view.py +307 -0
  57. diffract/core/data/nn/containers.py +86 -0
  58. diffract/core/data/nn/extractors/__init__.py +49 -0
  59. diffract/core/data/nn/extractors/base.py +300 -0
  60. diffract/core/data/nn/extractors/factory.py +234 -0
  61. diffract/core/data/nn/extractors/flax.py +112 -0
  62. diffract/core/data/nn/extractors/handlers/__init__.py +39 -0
  63. diffract/core/data/nn/extractors/handlers/base.py +110 -0
  64. diffract/core/data/nn/extractors/handlers/flax_handlers.py +71 -0
  65. diffract/core/data/nn/extractors/handlers/numpy_handlers.py +63 -0
  66. diffract/core/data/nn/extractors/handlers/onnx_handlers.py +67 -0
  67. diffract/core/data/nn/extractors/handlers/tensorflow_handlers.py +82 -0
  68. diffract/core/data/nn/extractors/handlers/torch_handlers.py +124 -0
  69. diffract/core/data/nn/extractors/interface.py +56 -0
  70. diffract/core/data/nn/extractors/numpy.py +94 -0
  71. diffract/core/data/nn/extractors/onnx.py +108 -0
  72. diffract/core/data/nn/extractors/tensorflow.py +99 -0
  73. diffract/core/data/nn/extractors/torch.py +204 -0
  74. diffract/core/data/nn/params/__init__.py +27 -0
  75. diffract/core/data/nn/params/interface.py +402 -0
  76. diffract/core/data/nn/params/metadata.py +110 -0
  77. diffract/core/data/nn/params/proxy.py +93 -0
  78. diffract/core/data/nn/params/repository.py +45 -0
  79. diffract/core/data/nn/params/schema.py +82 -0
  80. diffract/core/data/nn/params/view.py +393 -0
  81. diffract/core/data/proxy.py +246 -0
  82. diffract/core/data/repository.py +227 -0
  83. diffract/core/data/utils.py +33 -0
  84. diffract/core/data/view.py +389 -0
  85. diffract/core/export/__init__.py +27 -0
  86. diffract/core/export/containers.py +47 -0
  87. diffract/core/export/exporters.py +191 -0
  88. diffract/core/export/formatters/__init__.py +23 -0
  89. diffract/core/export/formatters/base.py +68 -0
  90. diffract/core/export/formatters/dict_formatter.py +38 -0
  91. diffract/core/export/formatters/json_formatter.py +58 -0
  92. diffract/core/export/formatters/list_formatter.py +41 -0
  93. diffract/core/export/formatters/pandas_formatter.py +86 -0
  94. diffract/core/export/formatters/polars_formatter.py +86 -0
  95. diffract/core/export/formatters/registry.py +84 -0
  96. diffract/core/export/interface.py +105 -0
  97. diffract/core/parallel/__init__.py +21 -0
  98. diffract/core/parallel/containers.py +57 -0
  99. diffract/core/parallel/execution.py +91 -0
  100. diffract/core/parallel/runtime.py +91 -0
  101. diffract/core/storage/__init__.py +35 -0
  102. diffract/core/storage/base_manager.py +330 -0
  103. diffract/core/storage/containers.py +122 -0
  104. diffract/core/storage/hdf5_manager.py +856 -0
  105. diffract/core/storage/hybrid_manager.py +218 -0
  106. diffract/core/storage/interface.py +222 -0
  107. diffract/core/storage/metadata.py +89 -0
  108. diffract/core/storage/ram_manager.py +159 -0
  109. diffract/core/storage/sqlite_manager.py +1062 -0
  110. diffract/core/storage/zarr_manager.py +992 -0
  111. diffract/core/utils/__init__.py +45 -0
  112. diffract/core/utils/build.py +46 -0
  113. diffract/core/utils/exceptions.py +11 -0
  114. diffract/core/utils/hashing.py +90 -0
  115. diffract/core/utils/imports.py +292 -0
  116. diffract/core/utils/math.py +15 -0
  117. diffract/py.typed +0 -0
  118. diffract/session/__init__.py +24 -0
  119. diffract/session/errors.py +17 -0
  120. diffract/session/field_cache.py +216 -0
  121. diffract/session/namespaces/__init__.py +15 -0
  122. diffract/session/namespaces/compute/__init__.py +219 -0
  123. diffract/session/namespaces/models/__init__.py +282 -0
  124. diffract/session/namespaces/models/parameters/__init__.py +133 -0
  125. diffract/session/namespaces/models/parameters/meta_patcher.py +167 -0
  126. diffract/session/namespaces/results/__init__.py +378 -0
  127. diffract/session/namespaces/results/eraser.py +129 -0
  128. diffract/session/namespaces/results/injester.py +249 -0
  129. diffract/session/namespaces/utils/__init__.py +141 -0
  130. diffract/session/namespaces/utils/merger.py +295 -0
  131. diffract/session/namespaces/viz/__init__.py +91 -0
  132. diffract/session/namespaces/viz/_utils.py +14 -0
  133. diffract/session/namespaces/viz/box.py +207 -0
  134. diffract/session/namespaces/viz/grid.py +160 -0
  135. diffract/session/namespaces/viz/heatmap.py +164 -0
  136. diffract/session/namespaces/viz/scatter.py +202 -0
  137. diffract/session/namespaces/viz/sparkline.py +270 -0
  138. diffract/session/namespaces/viz/violin.py +212 -0
  139. diffract/session/session.py +260 -0
  140. diffract/session/utils.py +81 -0
  141. diffract/viz/__init__.py +42 -0
  142. diffract/viz/data/__init__.py +34 -0
  143. diffract/viz/data/detection.py +57 -0
  144. diffract/viz/data/extraction.py +117 -0
  145. diffract/viz/data/filtering.py +57 -0
  146. diffract/viz/data/ordering.py +129 -0
  147. diffract/viz/data/provider.py +99 -0
  148. diffract/viz/data/types.py +98 -0
  149. diffract/viz/plots/__init__.py +37 -0
  150. diffract/viz/plots/base/__init__.py +20 -0
  151. diffract/viz/plots/base/axis.py +219 -0
  152. diffract/viz/plots/base/coloraxis.py +133 -0
  153. diffract/viz/plots/base/configurator.py +18 -0
  154. diffract/viz/plots/base/jitter.py +368 -0
  155. diffract/viz/plots/base/line.py +197 -0
  156. diffract/viz/plots/base/marker.py +278 -0
  157. diffract/viz/plots/base/overlay.py +14 -0
  158. diffract/viz/plots/base/plot.py +110 -0
  159. diffract/viz/plots/base/update.py +50 -0
  160. diffract/viz/plots/boxplot.py +283 -0
  161. diffract/viz/plots/cluster.py +374 -0
  162. diffract/viz/plots/heatmap.py +168 -0
  163. diffract/viz/plots/scatter.py +233 -0
  164. diffract/viz/plots/sparkline.py +405 -0
  165. diffract/viz/plots/subplots/__init__.py +32 -0
  166. diffract/viz/plots/subplots/coloraxis.py +339 -0
  167. diffract/viz/plots/subplots/factory.py +713 -0
  168. diffract/viz/plots/subplots/grid.py +214 -0
  169. diffract/viz/plots/subplots/layout.py +370 -0
  170. diffract/viz/plots/subplots/spec.py +39 -0
  171. diffract/viz/plots/violin.py +298 -0
  172. diffract/viz/renderer.py +513 -0
  173. diffract/viz/styling/__init__.py +78 -0
  174. diffract/viz/styling/palettes/__init__.py +20 -0
  175. diffract/viz/styling/palettes/bundle.py +16 -0
  176. diffract/viz/styling/palettes/color.py +83 -0
  177. diffract/viz/styling/palettes/dashes.py +27 -0
  178. diffract/viz/styling/palettes/symbols.py +42 -0
  179. diffract/viz/styling/resolvers/__init__.py +11 -0
  180. diffract/viz/styling/resolvers/categorical.py +68 -0
  181. diffract/viz/styling/resolvers/color.py +77 -0
  182. diffract/viz/styling/resolvers/numeric.py +108 -0
  183. diffract/viz/styling/sources.py +27 -0
  184. diffract/viz/styling/theme/__init__.py +29 -0
  185. diffract/viz/styling/theme/applier.py +114 -0
  186. diffract/viz/styling/theme/components.py +66 -0
  187. diffract/viz/styling/theme/presets.py +58 -0
  188. diffract/viz/styling/theme/theme.py +36 -0
  189. diffract_core-0.2.1.dist-info/METADATA +530 -0
  190. diffract_core-0.2.1.dist-info/RECORD +193 -0
  191. diffract_core-0.2.1.dist-info/WHEEL +4 -0
  192. diffract_core-0.2.1.dist-info/licenses/LICENSE +201 -0
  193. diffract_core-0.2.1.dist-info/licenses/NOTICE +2 -0
@@ -0,0 +1,45 @@
1
+ """Utility helpers used across diffract core.
2
+
3
+ This package provides essential utilities for:
4
+ - Object construction with default value handling
5
+ - Hash generation for short deterministic identifiers
6
+ - Optional dependency management and lazy imports
7
+ - Math utilities
8
+
9
+ Main components:
10
+ - build: Object construction helper with defaults
11
+ - hashing: Short deterministic IDs and unique ID generation
12
+ - imports: Optional dependency and lazy import helpers
13
+ - math: Mathematical utilities (mean, etc.)
14
+ """
15
+
16
+ from .build import build_with_defaults
17
+ from .hashing import HashUtils, get_unique_id
18
+ from .imports import (
19
+ LazyImport,
20
+ OptionalDependencyError,
21
+ available,
22
+ get_module,
23
+ is_available,
24
+ lazy_import,
25
+ optional_import,
26
+ require,
27
+ requires_package,
28
+ )
29
+ from .math import mean
30
+
31
+ __all__ = [
32
+ "HashUtils",
33
+ "LazyImport",
34
+ "OptionalDependencyError",
35
+ "available",
36
+ "build_with_defaults",
37
+ "get_module",
38
+ "get_unique_id",
39
+ "is_available",
40
+ "lazy_import",
41
+ "mean",
42
+ "optional_import",
43
+ "require",
44
+ "requires_package",
45
+ ]
@@ -0,0 +1,46 @@
1
+ """Object construction utilities.
2
+
3
+ Provides helpers for building objects from configuration mappings with
4
+ default value handling and parameter merging capabilities.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import TYPE_CHECKING, Any
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import Mapping
13
+
14
+ __all__ = ["build_with_defaults"]
15
+
16
+
17
+ def build_with_defaults[T](
18
+ cls: type[T], params: Mapping[str, Any] | None, **kwargs: Any
19
+ ) -> T:
20
+ """Construct an object filtering out None values from params.
21
+
22
+ Merges non-None items from params with explicit keyword overrides and
23
+ calls the class constructor with the resulting mapping.
24
+
25
+ Args:
26
+ cls: Class to instantiate.
27
+ params: Mapping of parameter names to values; None values are ignored.
28
+ If None, only kwargs are used.
29
+ **kwargs: Explicit overrides to merge atop filtered params.
30
+
31
+ Returns:
32
+ Newly constructed instance of cls.
33
+
34
+ Example:
35
+ >>> class Config:
36
+ ... def __init__(self, a: int, b: str = "default"):
37
+ ... self.a = a
38
+ ... self.b = b
39
+ >>> params = {"a": 1, "b": None, "c": "ignored"}
40
+ >>> obj = build_with_defaults(Config, params, b="override")
41
+ """
42
+ if params is None:
43
+ return cls(**kwargs)
44
+ clean = {k: v for k, v in params.items() if v is not None}
45
+ clean.update(**kwargs)
46
+ return cls(**clean)
@@ -0,0 +1,11 @@
1
+ """Exception formatting helpers for concise, consistent logs."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ def format_exception_message(exc: BaseException) -> str:
7
+ """Return a compact exception description for logs."""
8
+ message = str(exc).strip()
9
+ if message:
10
+ return f"{type(exc).__name__}: {message}"
11
+ return type(exc).__name__
@@ -0,0 +1,90 @@
1
+ """Hash utilities for short deterministic identifiers.
2
+
3
+ Provides utilities to create short hexadecimal hashes from strings or generic
4
+ objects via JSON serialization with fallbacks, plus a convenience unique
5
+ ID generator based on current time in nanoseconds.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import time
12
+ import zlib
13
+ from collections.abc import Iterable
14
+ from typing import Any
15
+
16
+ __all__ = ["HashUtils", "get_unique_id"]
17
+
18
+
19
+ class HashUtils:
20
+ """Hash utilities producing 8-character hexadecimal strings.
21
+
22
+ Provides methods to create deterministic short hashes from various data types
23
+ including strings and complex objects through JSON serialization.
24
+ """
25
+
26
+ @classmethod
27
+ def _prepare_data(cls, data: Any) -> str:
28
+ """Serialize arbitrary data to a string suitable for hashing.
29
+
30
+ Args:
31
+ data: Data to serialize (string, iterable, or any JSON-serializable object).
32
+
33
+ Returns:
34
+ String representation of the data ready for hashing.
35
+ """
36
+ if isinstance(data, str):
37
+ return data
38
+ if isinstance(data, Iterable):
39
+ serialized = json.dumps(tuple(data), sort_keys=True)
40
+ else:
41
+ try:
42
+ serialized = json.dumps(data, ensure_ascii=False)
43
+ except TypeError:
44
+ serialized = str(data)
45
+ return serialized
46
+
47
+ @classmethod
48
+ def _str_hash(cls, data: str) -> str:
49
+ """Compute 8-character hexadecimal CRC32 hash for a string.
50
+
51
+ Args:
52
+ data: String to hash.
53
+
54
+ Returns:
55
+ 8-character lowercase hexadecimal hash.
56
+ """
57
+ crc = zlib.crc32(data.encode("utf-8")) & 0xFFFFFFFF
58
+ return format(crc, "08x")
59
+
60
+ @classmethod
61
+ def get_obj_hash(cls, data: Any) -> str:
62
+ """Create deterministic short hash for any data structure.
63
+
64
+ Args:
65
+ data: Any data structure to hash.
66
+
67
+ Returns:
68
+ 8-character hexadecimal hash string.
69
+
70
+ Example:
71
+ >>> HashUtils.get_obj_hash("hello")
72
+ 'aaf4c61d'
73
+ >>> HashUtils.get_obj_hash([1, 2, 3])
74
+ '5289df73'
75
+ """
76
+ return cls._str_hash(cls._prepare_data(data))
77
+
78
+
79
+ def get_unique_id() -> str:
80
+ """Generate a unique ID using current time in nanoseconds.
81
+
82
+ Returns:
83
+ 8-character hexadecimal string based on current timestamp.
84
+
85
+ Example:
86
+ >>> uid = get_unique_id()
87
+ >>> len(uid)
88
+ 8
89
+ """
90
+ return HashUtils.get_obj_hash(time.time_ns())
@@ -0,0 +1,292 @@
1
+ """Optional dependency utilities and lazy import helpers.
2
+
3
+ Provides consistent helpers to check availability, import optionally with
4
+ fallbacks, enforce requirements with helpful errors, and decorate functions
5
+ that depend on optional packages.
6
+
7
+ The module also exposes module-level lazy flags like _IS_TORCH_AVAILABLE via
8
+ __getattr__ for convenience without upfront import cost.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import importlib
14
+ import logging
15
+ from functools import lru_cache, wraps
16
+ from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar
17
+
18
+ if TYPE_CHECKING:
19
+ from collections.abc import Callable
20
+ from types import ModuleType
21
+
22
+ __all__ = [
23
+ "LazyImport",
24
+ "OptionalDependencyError",
25
+ "available",
26
+ "get_module",
27
+ "is_available",
28
+ "lazy_import",
29
+ "optional_import",
30
+ "require",
31
+ "requires_package",
32
+ ]
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+ P = ParamSpec("P")
37
+ R = TypeVar("R")
38
+
39
+
40
+ class OptionalDependencyError(ImportError):
41
+ """Raised when an optional dependency is required but not installed."""
42
+
43
+
44
+ class LazyImport:
45
+ """Lazy import wrapper that defers module loading until first access.
46
+
47
+ Allows importing modules that may not be available at startup time,
48
+ with proper error handling when the module is actually needed.
49
+ """
50
+
51
+ def __init__(self, module_name: str) -> None:
52
+ """Initialize lazy import wrapper.
53
+
54
+ Args:
55
+ module_name: Name of the module to import lazily.
56
+ """
57
+ self._module_name = module_name
58
+ self._module: ModuleType | None = None
59
+ self._checked = False
60
+
61
+ def __getattr__(self, name: str) -> Any:
62
+ """Import module on first attribute access.
63
+
64
+ Args:
65
+ name: Attribute name to access from the module.
66
+
67
+ Returns:
68
+ Requested attribute from the imported module.
69
+
70
+ Raises:
71
+ OptionalDependencyError: If the module cannot be imported.
72
+ """
73
+ if not self._checked:
74
+ self._module = get_module(self._module_name)
75
+ self._checked = True
76
+
77
+ if self._module is None:
78
+ msg = (
79
+ f"Module '{self._module_name}' is required but not available. "
80
+ f"Install it with: pip install {self._module_name}"
81
+ )
82
+ raise OptionalDependencyError(msg)
83
+
84
+ return getattr(self._module, name)
85
+
86
+
87
+ class _AvailabilityFlags:
88
+ """Container for lazy availability flags."""
89
+
90
+ @property
91
+ def _IS_TORCH_AVAILABLE(self) -> bool: # noqa: N802
92
+ """Check if PyTorch is available."""
93
+ return is_available("torch")
94
+
95
+ @property
96
+ def _IS_PANDAS_AVAILABLE(self) -> bool: # noqa: N802
97
+ """Check if pandas is available."""
98
+ return is_available("pandas")
99
+
100
+ @property
101
+ def _IS_REDIS_AVAILABLE(self) -> bool: # noqa: N802
102
+ """Check if redis is available."""
103
+ return is_available("redis")
104
+
105
+
106
+ _flags = _AvailabilityFlags()
107
+
108
+
109
+ def __getattr__(name: str) -> Any:
110
+ """Provide lazy availability flags as module attributes.
111
+
112
+ Supports names like _IS_TORCH_AVAILABLE by delegating to _flags.
113
+
114
+ Args:
115
+ name: Attribute name to retrieve.
116
+
117
+ Returns:
118
+ Value of the requested attribute.
119
+
120
+ Raises:
121
+ AttributeError: If the attribute is not available.
122
+ """
123
+ if hasattr(_flags, name):
124
+ return getattr(_flags, name)
125
+ msg = f"module '{__name__}' has no attribute '{name}'"
126
+ raise AttributeError(msg)
127
+
128
+
129
+ @lru_cache(maxsize=128)
130
+ def is_available(package: str) -> bool:
131
+ """Check if a package can be imported.
132
+
133
+ Args:
134
+ package: Package name to check (e.g., "torch", "pandas").
135
+
136
+ Returns:
137
+ True if package is available, False otherwise.
138
+
139
+ Example:
140
+ >>> is_available("os") # Built-in module
141
+ True
142
+ >>> is_available("nonexistent_package")
143
+ False
144
+ """
145
+ try:
146
+ importlib.import_module(package)
147
+ except ImportError:
148
+ return False
149
+ else:
150
+ return True
151
+
152
+
153
+ def get_module(package: str) -> ModuleType | None:
154
+ """Import and return module or None if unavailable.
155
+
156
+ Args:
157
+ package: Package name to import.
158
+
159
+ Returns:
160
+ Imported module or None if import fails.
161
+
162
+ Example:
163
+ >>> module = get_module("os")
164
+ >>> module is not None
165
+ True
166
+ """
167
+ if is_available(package):
168
+ return importlib.import_module(package)
169
+ return None
170
+
171
+
172
+ def optional_import(package: str, *, fallback: Any = None) -> Any:
173
+ """Import a package with optional fallback.
174
+
175
+ Returns module or provided fallback if import fails.
176
+
177
+ Args:
178
+ package: Package name to import.
179
+ fallback: Value to return if import fails.
180
+
181
+ Returns:
182
+ Imported module or fallback value.
183
+
184
+ Example:
185
+ >>> result = optional_import("nonexistent", fallback="not found")
186
+ >>> result
187
+ 'not found'
188
+ """
189
+ module = get_module(package)
190
+ return module if module is not None else fallback
191
+
192
+
193
+ def require(package: str) -> ModuleType:
194
+ """Import a package or raise OptionalDependencyError.
195
+
196
+ Args:
197
+ package: Package name to import.
198
+
199
+ Returns:
200
+ Imported module.
201
+
202
+ Raises:
203
+ OptionalDependencyError: If package is not available.
204
+
205
+ Example:
206
+ >>> os_module = require("os")
207
+ >>> os_module.__name__
208
+ 'os'
209
+ """
210
+ if not is_available(package):
211
+ msg = (
212
+ f"Package '{package}' is required but not available. "
213
+ f"Install it with: pip install {package}"
214
+ )
215
+ raise OptionalDependencyError(msg)
216
+ return importlib.import_module(package)
217
+
218
+
219
+ def lazy_import(package: str) -> LazyImport:
220
+ """Create a lazy import wrapper for a package.
221
+
222
+ Args:
223
+ package: Package name to import lazily.
224
+
225
+ Returns:
226
+ LazyImport wrapper that will import the module on first access.
227
+
228
+ Example:
229
+ >>> torch = lazy_import("torch")
230
+ >>> # Module is not imported yet
231
+ >>> tensor = torch.tensor([1, 2, 3]) # Import happens here
232
+ """
233
+ return LazyImport(package)
234
+
235
+
236
+ def available(package: str) -> bool:
237
+ """Alias for is_available for backward compatibility.
238
+
239
+ Args:
240
+ package: Package name to check.
241
+
242
+ Returns:
243
+ True if package is available, False otherwise.
244
+ """
245
+ return is_available(package)
246
+
247
+
248
+ def requires_package[R](
249
+ package: str, *, fallback_return: R | None = None
250
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
251
+ """Decorator ensuring required package is available before function execution.
252
+
253
+ If the package is missing and fallback_return is provided, logs a warning
254
+ and returns fallback_return instead of raising.
255
+
256
+ Args:
257
+ package: Required package name.
258
+ fallback_return: Optional fallback value to return if package is missing.
259
+
260
+ Returns:
261
+ Decorator function.
262
+
263
+ Example:
264
+ >>> @requires_package("torch")
265
+ ... def create_tensor():
266
+ ... import torch
267
+ ...
268
+ ... return torch.tensor([1, 2, 3])
269
+ """
270
+
271
+ def decorator(func: Callable[P, R]) -> Callable[P, R]:
272
+ @wraps(func)
273
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
274
+ if not is_available(package):
275
+ if fallback_return is not None:
276
+ logger.warning(
277
+ "Function '%s' requires package '%s' which is not available. "
278
+ "Returning fallback value.",
279
+ func.__name__,
280
+ package,
281
+ )
282
+ return fallback_return
283
+ msg = (
284
+ f"Function '{func.__name__}' requires package '{package}' "
285
+ f"which is not available. Install it with: pip install {package}"
286
+ )
287
+ raise OptionalDependencyError(msg)
288
+ return func(*args, **kwargs)
289
+
290
+ return wrapper
291
+
292
+ return decorator
@@ -0,0 +1,15 @@
1
+ """Math utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterable
6
+
7
+
8
+ def mean(values: Iterable[float]) -> float:
9
+ """Return arithmetic mean, or 0.0 for empty iterables."""
10
+ total = 0.0
11
+ n = 0
12
+ for v in values:
13
+ total += float(v)
14
+ n += 1
15
+ return total / n if n > 0 else 0.0
diffract/py.typed ADDED
File without changes
@@ -0,0 +1,24 @@
1
+ """Public session API and namespace facade."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from diffract.core.data.nn.extractors.base import ParameterOverrides
6
+ from diffract.core.data.nn.params.schema import ParameterType
7
+
8
+ from .errors import (
9
+ KernelNotFoundError,
10
+ ModelAlreadyExistsError,
11
+ ModelNotFoundError,
12
+ SessionError,
13
+ )
14
+ from .session import Session
15
+
16
+ __all__ = [
17
+ "KernelNotFoundError",
18
+ "ModelAlreadyExistsError",
19
+ "ModelNotFoundError",
20
+ "ParameterOverrides",
21
+ "ParameterType",
22
+ "Session",
23
+ "SessionError",
24
+ ]
@@ -0,0 +1,17 @@
1
+ """Session-level error types."""
2
+
3
+
4
+ class SessionError(Exception):
5
+ """Base exception class for session-related errors."""
6
+
7
+
8
+ class ModelNotFoundError(SessionError):
9
+ """Raised when a model is not found in the session."""
10
+
11
+
12
+ class ModelAlreadyExistsError(SessionError):
13
+ """Raised when a model with the same id already exists."""
14
+
15
+
16
+ class KernelNotFoundError(SessionError):
17
+ """Raised when a kernel is not found in the registry."""