diff --git a/docs/api.rst b/docs/api.rst index 6f5dc61..f26ef23 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -65,6 +65,7 @@ override the global seed to support seed-specifying APIs. Packages that expect their client code to use SeedBank to seed the random number ecosystem should use these functions to obtain random number generators. +.. autofunction:: std_rng .. autofunction:: numpy_rng .. autofunction:: numpy_random_state .. autofunction:: cupy_rng diff --git a/seedbank/__init__.py b/seedbank/__init__.py index 9310889..8b8c4e5 100644 --- a/seedbank/__init__.py +++ b/seedbank/__init__.py @@ -31,6 +31,7 @@ "initialize", "init_file", "derive_seed", + "std_rng", "numpy_rng", "numpy_random_state", "cupy_rng", @@ -136,3 +137,4 @@ def int_seed( from seedbank._config import init_file # noqa: E402 from seedbank.cupy import cupy_rng # noqa: E402 from seedbank.numpy import numpy_random_state, numpy_rng # noqa: E402 +from seedbank.stdlib import std_rng # noqa: E402 diff --git a/seedbank/stdlib.py b/seedbank/stdlib.py index 95e47e0..1169956 100644 --- a/seedbank/stdlib.py +++ b/seedbank/stdlib.py @@ -1,6 +1,9 @@ import logging import random +from typing import Optional +from . import derive_seed +from ._keys import SeedLike, make_seed from ._state import SeedState _log = logging.getLogger(__name__) @@ -13,3 +16,25 @@ def is_available(): def seed(state: SeedState): _log.debug("initializing stdlib seed") random.seed(state.int_seed) + + +def std_rng( + spec: Optional[SeedLike] = None, +) -> random.Random: + """ + Get a standard library random number generator (:class:`random.Random`) with + either the specified seed or a fresh seed. + + Args: + spec: + The spec for this RNG. + + Returns: + A random number generator. + """ + if spec is None: + seed = derive_seed() + else: + seed = make_seed(spec) + data = seed.generate_state(4) + return random.Random(bytes(data)) diff --git a/tests/test_stdlib.py b/tests/test_stdlib.py new file mode 100644 index 0000000..22d52e4 --- /dev/null +++ b/tests/test_stdlib.py @@ -0,0 +1,33 @@ +""" +stdlib python tests +""" + +import random + +from seedbank import std_rng + + +def test_stdlib_rng(): + """ + Make sure we get an stdlib RNG. + """ + rng = std_rng() + assert isinstance(rng, random.Random) + + +def test_stdlib_rng_fresh_seed(): + """ + Test that two stdlib RNGs with fresh seeds return different numbers. + """ + rng1 = std_rng() + rng2 = std_rng() + assert rng1.getrandbits(10) != rng2.getrandbits(10) + + +def test_stdlib_rng_same_seed(): + """ + Test that two stdlib RNGs with the same seed start the same. + """ + rng1 = std_rng("foo") + rng2 = std_rng("foo") + assert rng1.getrandbits(10) == rng2.getrandbits(10)