Skip to content

Commit fa17a5e

Browse files
committed
feat: add advanced features (async, persistence, pools, lazy)
- aio.py: Async support with await trueentropy.aio.random() - persistence.py: Save/load pool state (binary and JSON) - pools.py: PoolManager for multiple isolated pools - lazy.py: LazyHarvesters for on-demand loading - pool.py: Added persistence support methods
1 parent 4635570 commit fa17a5e

5 files changed

Lines changed: 1115 additions & 0 deletions

File tree

src/trueentropy/aio.py

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
# =============================================================================
2+
# TrueEntropy - Async Support Module
3+
# =============================================================================
4+
#
5+
# This module provides async/await support for TrueEntropy.
6+
# All random generation functions have async versions that can be
7+
# used in asyncio applications.
8+
#
9+
# Usage:
10+
# import asyncio
11+
# import trueentropy.aio as te_async
12+
#
13+
# async def main():
14+
# value = await te_async.random()
15+
# number = await te_async.randint(1, 100)
16+
#
17+
# asyncio.run(main())
18+
#
19+
# =============================================================================
20+
21+
"""
22+
Async support for TrueEntropy.
23+
24+
Provides async versions of all random generation functions for use
25+
in asyncio applications.
26+
"""
27+
28+
from __future__ import annotations
29+
30+
import asyncio
31+
from typing import Any, MutableSequence, Sequence, TypeVar
32+
33+
from trueentropy.pool import EntropyPool
34+
from trueentropy.tap import EntropyTap
35+
36+
37+
T = TypeVar("T")
38+
39+
# Global async-safe pool and tap
40+
_async_pool: EntropyPool = EntropyPool()
41+
_async_tap: EntropyTap = EntropyTap(_async_pool)
42+
_async_lock: asyncio.Lock | None = None
43+
44+
45+
def _get_lock() -> asyncio.Lock:
46+
"""Get or create the async lock."""
47+
global _async_lock
48+
if _async_lock is None:
49+
_async_lock = asyncio.Lock()
50+
return _async_lock
51+
52+
53+
# =============================================================================
54+
# Async Random Value Generation
55+
# =============================================================================
56+
57+
58+
async def random() -> float:
59+
"""
60+
Async version of trueentropy.random().
61+
62+
Returns:
63+
A float value where 0.0 <= value < 1.0
64+
65+
Example:
66+
>>> import asyncio
67+
>>> import trueentropy.aio as te
68+
>>> asyncio.run(te.random())
69+
0.7234891623...
70+
"""
71+
async with _get_lock():
72+
# Run in executor to avoid blocking event loop
73+
loop = asyncio.get_event_loop()
74+
return await loop.run_in_executor(None, _async_tap.random)
75+
76+
77+
async def randint(a: int, b: int) -> int:
78+
"""
79+
Async version of trueentropy.randint().
80+
81+
Args:
82+
a: Lower bound (inclusive)
83+
b: Upper bound (inclusive)
84+
85+
Returns:
86+
Random integer in [a, b]
87+
"""
88+
async with _get_lock():
89+
loop = asyncio.get_event_loop()
90+
return await loop.run_in_executor(None, _async_tap.randint, a, b)
91+
92+
93+
async def randbool() -> bool:
94+
"""
95+
Async version of trueentropy.randbool().
96+
97+
Returns:
98+
True or False with equal probability
99+
"""
100+
async with _get_lock():
101+
loop = asyncio.get_event_loop()
102+
return await loop.run_in_executor(None, _async_tap.randbool)
103+
104+
105+
async def randbytes(n: int) -> bytes:
106+
"""
107+
Async version of trueentropy.randbytes().
108+
109+
Args:
110+
n: Number of bytes to generate
111+
112+
Returns:
113+
A bytes object of length n
114+
"""
115+
async with _get_lock():
116+
loop = asyncio.get_event_loop()
117+
return await loop.run_in_executor(None, _async_tap.randbytes, n)
118+
119+
120+
async def choice(seq: Sequence[T]) -> T:
121+
"""
122+
Async version of trueentropy.choice().
123+
124+
Args:
125+
seq: A non-empty sequence
126+
127+
Returns:
128+
A randomly selected element
129+
"""
130+
async with _get_lock():
131+
loop = asyncio.get_event_loop()
132+
return await loop.run_in_executor(None, _async_tap.choice, seq)
133+
134+
135+
async def shuffle(seq: MutableSequence[Any]) -> None:
136+
"""
137+
Async version of trueentropy.shuffle().
138+
139+
Args:
140+
seq: A mutable sequence to shuffle in-place
141+
"""
142+
async with _get_lock():
143+
loop = asyncio.get_event_loop()
144+
await loop.run_in_executor(None, _async_tap.shuffle, seq)
145+
146+
147+
async def sample(seq: Sequence[T], k: int) -> list[T]:
148+
"""
149+
Async version of trueentropy.sample().
150+
151+
Args:
152+
seq: The sequence to sample from
153+
k: Number of unique elements to select
154+
155+
Returns:
156+
A list of k unique elements
157+
"""
158+
async with _get_lock():
159+
loop = asyncio.get_event_loop()
160+
return await loop.run_in_executor(None, _async_tap.sample, seq, k)
161+
162+
163+
async def random_uuid() -> str:
164+
"""
165+
Async version of trueentropy.random_uuid().
166+
167+
Returns:
168+
A UUID v4 string
169+
"""
170+
async with _get_lock():
171+
loop = asyncio.get_event_loop()
172+
return await loop.run_in_executor(None, _async_tap.random_uuid)
173+
174+
175+
async def random_token(length: int = 32, encoding: str = "hex") -> str:
176+
"""
177+
Async version of trueentropy.random_token().
178+
179+
Args:
180+
length: Number of random bytes
181+
encoding: 'hex' or 'base64'
182+
183+
Returns:
184+
A random token string
185+
"""
186+
async with _get_lock():
187+
loop = asyncio.get_event_loop()
188+
return await loop.run_in_executor(
189+
None, _async_tap.random_token, length, encoding
190+
)
191+
192+
193+
async def random_password(
194+
length: int = 16,
195+
charset: str | None = None,
196+
include_uppercase: bool = True,
197+
include_lowercase: bool = True,
198+
include_digits: bool = True,
199+
include_symbols: bool = True
200+
) -> str:
201+
"""
202+
Async version of trueentropy.random_password().
203+
204+
Args:
205+
length: Password length
206+
charset: Custom character set
207+
include_*: Character type flags
208+
209+
Returns:
210+
A random password string
211+
"""
212+
async with _get_lock():
213+
loop = asyncio.get_event_loop()
214+
return await loop.run_in_executor(
215+
None,
216+
lambda: _async_tap.random_password(
217+
length, charset, include_uppercase,
218+
include_lowercase, include_digits, include_symbols
219+
)
220+
)
221+
222+
223+
# =============================================================================
224+
# Async Distributions
225+
# =============================================================================
226+
227+
228+
async def uniform(a: float, b: float) -> float:
229+
"""Async version of trueentropy.uniform()."""
230+
async with _get_lock():
231+
loop = asyncio.get_event_loop()
232+
return await loop.run_in_executor(None, _async_tap.uniform, a, b)
233+
234+
235+
async def gauss(mu: float = 0.0, sigma: float = 1.0) -> float:
236+
"""Async version of trueentropy.gauss()."""
237+
async with _get_lock():
238+
loop = asyncio.get_event_loop()
239+
return await loop.run_in_executor(None, _async_tap.gauss, mu, sigma)
240+
241+
242+
async def triangular(
243+
low: float = 0.0, high: float = 1.0, mode: float | None = None
244+
) -> float:
245+
"""Async version of trueentropy.triangular()."""
246+
async with _get_lock():
247+
loop = asyncio.get_event_loop()
248+
return await loop.run_in_executor(
249+
None, _async_tap.triangular, low, high, mode
250+
)
251+
252+
253+
async def exponential(lambd: float = 1.0) -> float:
254+
"""Async version of trueentropy.exponential()."""
255+
async with _get_lock():
256+
loop = asyncio.get_event_loop()
257+
return await loop.run_in_executor(None, _async_tap.exponential, lambd)
258+
259+
260+
async def weighted_choice(seq: Sequence[T], weights: Sequence[float]) -> T:
261+
"""Async version of trueentropy.weighted_choice()."""
262+
async with _get_lock():
263+
loop = asyncio.get_event_loop()
264+
return await loop.run_in_executor(
265+
None, _async_tap.weighted_choice, seq, weights
266+
)
267+
268+
269+
# =============================================================================
270+
# Module Exports
271+
# =============================================================================
272+
273+
__all__ = [
274+
"random",
275+
"randint",
276+
"randbool",
277+
"randbytes",
278+
"choice",
279+
"shuffle",
280+
"sample",
281+
"random_uuid",
282+
"random_token",
283+
"random_password",
284+
"uniform",
285+
"gauss",
286+
"triangular",
287+
"exponential",
288+
"weighted_choice",
289+
]

0 commit comments

Comments
 (0)