Skip to content

Commit

Permalink
docs: Add a tutorial on debugging an infinite loop
Browse files Browse the repository at this point in the history
Signed-off-by: Gwen Sarapata <[email protected]>
  • Loading branch information
gwen-sarapata authored and godlygeek committed Jul 23, 2024
1 parent 0d084a9 commit 2657eb6
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Contents
:caption: Hands-on Tutorial

tutorials/deadlock
tutorials/random_prime_number

.. toctree::
:caption: Project Information
Expand Down
54 changes: 54 additions & 0 deletions docs/tutorials/random_prime_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import random
from math import sqrt


def is_prime(number, *, _factor=None):
"""
Check whether the given number is prime.
Returns:
True if prime, False if composite.
"""
# If we weren't called recursively, generate the initial factor to check
if _factor is None:
_factor = int(sqrt(number)) + 1

# Prime number must be integers
if type(number) is not int:
return False

# If we reached 1 without finding another divisor, it's prime!
if _factor == 1:
return True

# If any other factor evenly goes into the number, it is not prime
if number % _factor == 0:
return False

# Recursively check with smaller factor
return is_prime(number, _factor=_factor - 1)


def generate_num():
"""
Generates a random integer
Returns:
A 2-tuple containing the randomly generated number
and whether or not it's prime.
"""
number = random.random()
return number, is_prime(number)


def main():
# Keep generating random numbers until we find a prime.
number, prime = generate_num()
while not prime:
number, prime = generate_num()

print(f"The prime number generated is: {number}")


if __name__ == "__main__":
main()
98 changes: 98 additions & 0 deletions docs/tutorials/random_prime_number.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
Prime Number Generation
=======================

Intro
-----

Error handling is essential for any programmer. Being able to understand error messages and quickly
apply accurate fixes is vital for mental health. Unfortunately, Python's stack traces don't always
give a clear picture of everything that can go wrong...

Working Through an Example
^^^^^^^^^^^^^^^^^^^^^^^^^^

Let's go through an example where we create a function to identify prime numbers, and then generate
random numbers until we find a prime number validated through our function.

Exercise
""""""""

Take a look at the example in ``random_prime_number.py``: do you see any errors in the code?

.. literalinclude:: random_prime_number.py
:linenos:

Take a guess, and then confirm by running the program. Does it work as you expected? If not, is it
obvious why your program is not running properly?

Expectations vs Reality
"""""""""""""""""""""""

Often an exception is thrown and the stack trace Python prints allows a quick solution to become
apparent, but sometimes we get no feedback at all. In this case, we accidentally entered an infinite
loop! But where in the program did we go wrong? Does the logic in the function ``is_prime`` work
correctly, or is the error somewhere else?

Since the code enters an infinite loop, Python never gives us a stack trace. This can lead to a lot
of confusion and no information as to where the error occurred. We're here to see if there is
a better way to debug your infinite loop.

Challenge
"""""""""

Use ``pystack`` on the running program and see if you can identify the bug. Be sure to check what
options may help you locate the errors in your code.

.. raw:: html

<details>
<summary><i>Hint 1</i></summary>

Check your local variables using ``pystack``.

.. raw:: html

</details>

.. raw:: html

<details>
<summary><i>Hint 2</i></summary>

Try the following command while your program is running: ``pystack remote --locals {PID}``.

.. raw:: html

</details>

Solutions
"""""""""

.. raw:: html

<details>
<summary><i>Toggle to see the solution</i></summary>

After using PyStack and printing the locals you can see that we accidentally used the wrong random
function! In our current implementation, ``random.random()`` only returns floats between [0.0, 1.0).
Our ``is_prime`` function has correct logic, but the function will never get passed a prime number,
because of the random number generator we chose!

.. literalinclude:: random_prime_number.py
:linenos:
:pyobject: generate_num
:emphasize-lines: 9

A quick fix is to replace the ``random.random()`` call with ``random.randint(0, 100000)``. With this
quick fix, our program will now generate random prime numbers!

.. raw:: html

</details>

Conclusion
^^^^^^^^^^

Sometimes, finding the bug in your code is not as simple as looking at a stack trace. Infinite loops
are common logical errors in programs, but can be difficult to find. Instead of spending excessive
time searching through lots of code, using PyStack can help you find these logical errors.

0 comments on commit 2657eb6

Please sign in to comment.