runid.py 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738
  1. """runid util."""
  2. import os
  3. import random
  4. import secrets
  5. from string import ascii_lowercase, digits
  6. _ID_CHARS = f"{ascii_lowercase}{digits}"
  7. # Create a dedicated Random instance with its own state so it
  8. # is not affected by global random.seed() calls
  9. _random = random.Random()
  10. # Reset the random number generator on forking a new process to avoid multiple processes using the same seed.
  11. # The `random` module basically does this internally for python's global random state.
  12. # This is only necessary on platforms that support the `fork` multiprocessing start method (e.g. POSIX).
  13. if hasattr(os, "fork"):
  14. os.register_at_fork(after_in_child=_random.seed)
  15. def generate_id(length: int = 8) -> str:
  16. """Generate a random base-36 string of `length` digits."""
  17. # There are ~2.8T base-36 8-digit strings. If we generate 210k ids,
  18. # we'll have a ~1% chance of collision.
  19. return "".join(secrets.choice(_ID_CHARS) for _ in range(length))
  20. def generate_fast_id(length: int = 8) -> str:
  21. """Faster alternative to `generate_id` if cryptographic strength isn't needed.
  22. In local testing at the time of implementation, this is ~30-50x faster than
  23. `generate_id` when generating 128-character IDs.
  24. Uses a dedicated Random instance to avoid being affected by global
  25. random.seed() calls from user code or libraries.
  26. """
  27. return "".join(_random.choices(_ID_CHARS, k=length))