Skip to content

Commit 6bc2f96

Browse files
committed
feat(config): add configuration module for offline mode
1 parent 9f5aa6d commit 6bc2f96

1 file changed

Lines changed: 297 additions & 0 deletions

File tree

src/trueentropy/config.py

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
# =============================================================================
2+
# TrueEntropy - Configuration Module
3+
# =============================================================================
4+
#
5+
# This module provides configuration options for the TrueEntropy library.
6+
# It allows users to enable/disable specific entropy harvesters and
7+
# configure offline mode for environments without network access.
8+
#
9+
# Usage:
10+
# import trueentropy
11+
#
12+
# # Enable offline mode (disables all network-dependent harvesters)
13+
# trueentropy.configure(offline_mode=True)
14+
#
15+
# # Or configure individual harvesters
16+
# trueentropy.configure(enable_network=False, enable_weather=False)
17+
#
18+
# =============================================================================
19+
20+
"""
21+
Configuration for TrueEntropy entropy harvesting.
22+
23+
Provides a configuration system to enable/disable individual entropy
24+
sources and support offline mode operation.
25+
"""
26+
27+
from __future__ import annotations
28+
29+
from dataclasses import dataclass
30+
31+
32+
# -----------------------------------------------------------------------------
33+
# Source Metadata
34+
# -----------------------------------------------------------------------------
35+
36+
# Categorize sources by their network requirements
37+
OFFLINE_SOURCES = frozenset({"timing", "system"})
38+
NETWORK_SOURCES = frozenset({"network", "external", "weather", "radioactive"})
39+
ALL_SOURCES = OFFLINE_SOURCES | NETWORK_SOURCES
40+
41+
42+
# -----------------------------------------------------------------------------
43+
# Configuration Dataclass
44+
# -----------------------------------------------------------------------------
45+
46+
47+
@dataclass
48+
class TrueEntropyConfig:
49+
"""
50+
Configuration for TrueEntropy entropy collection.
51+
52+
This dataclass holds all configuration options for the library.
53+
Users can enable/disable individual harvesters or use offline_mode
54+
to disable all network-dependent sources at once.
55+
56+
Attributes:
57+
enable_timing: Enable CPU timing jitter harvester (offline)
58+
enable_system: Enable system state harvester (offline)
59+
enable_network: Enable network latency harvester (requires network)
60+
enable_external: Enable external API harvester (requires network)
61+
enable_weather: Enable weather data harvester (requires network)
62+
enable_radioactive: Enable quantum/radioactive harvester (requires network)
63+
64+
Example:
65+
>>> from trueentropy.config import TrueEntropyConfig
66+
>>> config = TrueEntropyConfig(offline_mode=True)
67+
>>> config.enable_network
68+
False
69+
"""
70+
71+
# Offline sources (always available)
72+
enable_timing: bool = True
73+
enable_system: bool = True
74+
75+
# Network-dependent sources
76+
enable_network: bool = True
77+
enable_external: bool = True
78+
enable_weather: bool = True
79+
enable_radioactive: bool = True
80+
81+
def __post_init__(self) -> None:
82+
"""Validate configuration after initialization."""
83+
# Ensure at least one source is enabled
84+
if not any(self.enabled_sources):
85+
raise ValueError(
86+
"At least one entropy source must be enabled. "
87+
"Cannot disable all harvesters."
88+
)
89+
90+
# -------------------------------------------------------------------------
91+
# Properties
92+
# -------------------------------------------------------------------------
93+
94+
@property
95+
def offline_mode(self) -> bool:
96+
"""
97+
Check if running in offline mode.
98+
99+
Returns True if all network-dependent sources are disabled.
100+
"""
101+
return not any([
102+
self.enable_network,
103+
self.enable_external,
104+
self.enable_weather,
105+
self.enable_radioactive,
106+
])
107+
108+
@property
109+
def enabled_sources(self) -> set[str]:
110+
"""
111+
Get the set of currently enabled source names.
112+
113+
Returns:
114+
Set of enabled source names (e.g., {"timing", "system"})
115+
"""
116+
sources = set()
117+
if self.enable_timing:
118+
sources.add("timing")
119+
if self.enable_system:
120+
sources.add("system")
121+
if self.enable_network:
122+
sources.add("network")
123+
if self.enable_external:
124+
sources.add("external")
125+
if self.enable_weather:
126+
sources.add("weather")
127+
if self.enable_radioactive:
128+
sources.add("radioactive")
129+
return sources
130+
131+
@property
132+
def disabled_sources(self) -> set[str]:
133+
"""
134+
Get the set of currently disabled source names.
135+
136+
Returns:
137+
Set of disabled source names
138+
"""
139+
return ALL_SOURCES - self.enabled_sources
140+
141+
# -------------------------------------------------------------------------
142+
# Methods
143+
# -------------------------------------------------------------------------
144+
145+
def get_source_info(self, source: str) -> dict[str, bool]:
146+
"""
147+
Get information about a specific source.
148+
149+
Args:
150+
source: Name of the source (e.g., "timing", "network")
151+
152+
Returns:
153+
Dict with 'enabled' and 'requires_network' keys
154+
"""
155+
enabled_map = {
156+
"timing": self.enable_timing,
157+
"system": self.enable_system,
158+
"network": self.enable_network,
159+
"external": self.enable_external,
160+
"weather": self.enable_weather,
161+
"radioactive": self.enable_radioactive,
162+
}
163+
164+
return {
165+
"enabled": enabled_map.get(source, False),
166+
"requires_network": source in NETWORK_SOURCES,
167+
}
168+
169+
def copy(self, **changes) -> "TrueEntropyConfig":
170+
"""
171+
Create a copy of this config with optional changes.
172+
173+
Args:
174+
**changes: Config attributes to override
175+
176+
Returns:
177+
New TrueEntropyConfig instance
178+
"""
179+
from dataclasses import asdict
180+
current = asdict(self)
181+
current.update(changes)
182+
return TrueEntropyConfig(**current)
183+
184+
185+
# -----------------------------------------------------------------------------
186+
# Global Configuration
187+
# -----------------------------------------------------------------------------
188+
189+
# Global configuration instance (used by default)
190+
_global_config: TrueEntropyConfig = TrueEntropyConfig()
191+
192+
193+
def get_config() -> TrueEntropyConfig:
194+
"""
195+
Get the current global configuration.
196+
197+
Returns:
198+
The global TrueEntropyConfig instance
199+
"""
200+
return _global_config
201+
202+
203+
def configure(
204+
*,
205+
offline_mode: bool | None = None,
206+
enable_timing: bool | None = None,
207+
enable_system: bool | None = None,
208+
enable_network: bool | None = None,
209+
enable_external: bool | None = None,
210+
enable_weather: bool | None = None,
211+
enable_radioactive: bool | None = None,
212+
) -> TrueEntropyConfig:
213+
"""
214+
Configure TrueEntropy globally.
215+
216+
This function updates the global configuration used by all
217+
entropy collection functions. You can either set offline_mode=True
218+
to disable all network sources, or configure individual sources.
219+
220+
Args:
221+
offline_mode: If True, disables all network-dependent sources.
222+
If False, enables all sources. If None, ignored.
223+
enable_timing: Enable/disable CPU timing harvester
224+
enable_system: Enable/disable system state harvester
225+
enable_network: Enable/disable network latency harvester
226+
enable_external: Enable/disable external API harvester
227+
enable_weather: Enable/disable weather data harvester
228+
enable_radioactive: Enable/disable quantum randomness harvester
229+
230+
Returns:
231+
The updated global configuration
232+
233+
Example:
234+
>>> import trueentropy
235+
>>> # Enable offline mode
236+
>>> trueentropy.configure(offline_mode=True)
237+
>>> # Or disable specific sources
238+
>>> trueentropy.configure(enable_weather=False, enable_radioactive=False)
239+
"""
240+
global _global_config
241+
242+
# Start with current config values
243+
new_config = {
244+
"enable_timing": _global_config.enable_timing,
245+
"enable_system": _global_config.enable_system,
246+
"enable_network": _global_config.enable_network,
247+
"enable_external": _global_config.enable_external,
248+
"enable_weather": _global_config.enable_weather,
249+
"enable_radioactive": _global_config.enable_radioactive,
250+
}
251+
252+
# Handle offline_mode convenience flag
253+
if offline_mode is True:
254+
# Disable all network sources
255+
new_config["enable_network"] = False
256+
new_config["enable_external"] = False
257+
new_config["enable_weather"] = False
258+
new_config["enable_radioactive"] = False
259+
elif offline_mode is False:
260+
# Enable all sources
261+
new_config["enable_timing"] = True
262+
new_config["enable_system"] = True
263+
new_config["enable_network"] = True
264+
new_config["enable_external"] = True
265+
new_config["enable_weather"] = True
266+
new_config["enable_radioactive"] = True
267+
268+
# Apply individual overrides (these take precedence)
269+
if enable_timing is not None:
270+
new_config["enable_timing"] = enable_timing
271+
if enable_system is not None:
272+
new_config["enable_system"] = enable_system
273+
if enable_network is not None:
274+
new_config["enable_network"] = enable_network
275+
if enable_external is not None:
276+
new_config["enable_external"] = enable_external
277+
if enable_weather is not None:
278+
new_config["enable_weather"] = enable_weather
279+
if enable_radioactive is not None:
280+
new_config["enable_radioactive"] = enable_radioactive
281+
282+
# Create and validate new config
283+
_global_config = TrueEntropyConfig(**new_config)
284+
285+
return _global_config
286+
287+
288+
def reset_config() -> TrueEntropyConfig:
289+
"""
290+
Reset configuration to defaults (all sources enabled).
291+
292+
Returns:
293+
The reset global configuration
294+
"""
295+
global _global_config
296+
_global_config = TrueEntropyConfig()
297+
return _global_config

0 commit comments

Comments
 (0)