July 9th, 2024

Making Python Less Random

Andrew Healey discussed debugging a Python game prototype with randomness bugs. He used ptrace to control system calls, ensuring consistent results for random functions without altering the code, enabling deterministic debugging.

Read original articleLink Icon
Making Python Less Random

In July 2024, Andrew Healey shared his experience of debugging a Python game prototype with tricky randomness bugs. He identified sources of randomness in his code and explored methods to achieve deterministic debugging without altering the code. By intercepting system calls using ptrace, he controlled the output of getrandom calls, ensuring consistent results for os.urandom and random.randint functions. Through a detailed explanation of the process, he demonstrated how to manipulate the Python process to return specific values, enabling deterministic outcomes for random number generation. This approach allowed for predictable results each time the Python process restarted, showcasing a method to handle randomness in Python programs without modifying the code directly. Andrew's exploration of system call tracing and manipulation provides insights into the inner workings of Python's randomness and offers a practical solution for deterministic debugging in Python applications.

Link Icon 3 comments
By @sgarland - 6 months
While I appreciate examining system calls, I feel like I must be missing something in TFA, because you can just seed your own instance of random.Random [0].

Trivial (and terrible) example – on every run past the first, the lists are identical:

  #!/usr/bin/env python3

  import pickle
  import random

  SEED = 42

  if __name__ == "__main__":
      r = random.Random(SEED)
      rand_l = None
      new_rand_l = []
      try:
          with open("rand_list.dat", "rb") as f:
              rand_l = pickle.load(f)
      except FileNotFoundError:
          pass
      for i in range(100):
          new_rand_l.append(r.randint(0, 1000))
      if rand_l is not None:
          assert rand_l == new_rand_l
      else:
          with open("rand_list.dat", "wb") as f:
              pickle.dump(new_rand_l, f)

[0]: https://docs.python.org/3/library/random.html#random.Random....
By @Numerlor - 6 months
Why not just patch the module in the entry point beforen others get a chance to import the preseeded methods?
By @ocular-rockular - 6 months
Just set seed ._.