What are you even talking about?
Let’s say I want to add some Python code to a large repo. Maybe I want to add a function that returns the nth multiple of 2. The test suite will probably catch any mistakes I make.
def get_nth_multiple_of_2(data):
return data * 2
Send it!
$ python -m unittest
F...............
======================================================================
FAIL: test_1 (test_t.TestMyCode)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/user/test_t.py", line 6, in test_1
self.assertEqual(main(), [0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
AssertionError: Lists differ: ['00', '11', '22', '33', '44', '55', '66', '77', '88', '99'] != [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
First differing element 0:
'00'
0
- ['00', '11', '22', '33', '44', '55', '66', '77', '88', '99']
+ [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
----------------------------------------------------------------------
Ran 16 tests in 0.002s
FAILED (failures=1)
Uh oh.
What’s going on?
Let’s throw in a breakpoint()
.
def get_nth_multiple_of_2(data):
breakpoint()
return data * 2
And give it another go . . .
$ python -m unittest
> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) data
'0'
(Pdb) type(data)
<class 'str'>
(Pdb)
Oops.
Looks like something somewhere in the repo is calling this function with strings instead of integers.
Should be a pretty quick fix—just need to close pdb
and tweak a line or two.
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb)
No, pdb
, I figured out the problem.
I don’t need the debugger anymore.
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb)
No, no, I said, exit
.
Please exit.
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb)
Exit?
Please, pdb
?
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb)
What do you want from me? ctrl+d
?
(Pdb) exit
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb)
E> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb)
Have it your way, then. ctrl+c
it is.
(Pdb) exit
> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) --KeyboardInterrupt--
(Pdb)
Oh, a funny guy?
$ ps aux | grep unittest
user 4952 0.0 0.1 50960 13732 pts/2 S+ 10:39 0:00 /home/user/.pyenv/versions/3.10.5/bin/python -m unittest
$ kill 4952
(Pdb) Terminated
$
I thought so.
That was terrible! How can I never experience that ever again?
If you switch into interactive debugging and then exit()
that interpreter, everything comes grinding to a halt.
$ python -m unittest
> /home/user/t.py(8)get_nth_multiple_of_2()
-> return data * 2
(Pdb) interact
*interactive*
>>> exit()
<output truncated to only show one failing test>
ERROR: test_9 (test_t.TestMyCode)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/user/test_t.py", line 33, in test_9
main()
File "/home/user/t.py", line 3, in main
processed_data = [get_nth_multiple_of_2(x) for x in data]
File "/home/user/t.py", line 3, in <listcomp>
processed_data = [get_nth_multiple_of_2(x) for x in data]
File "/home/user/t.py", line 8, in get_nth_multiple_of_2
return data * 2
File "/home/user/t.py", line 8, in get_nth_multiple_of_2
return data * 2
File "/home/user/.pyenv/versions/3.10.5/lib/python3.10/bdb.py", line 90, in trace_dispatch
return self.dispatch_line(frame)
File "/home/user/.pyenv/versions/3.10.5/lib/python3.10/bdb.py", line 114, in dispatch_line
self.user_line(frame)
File "/home/user/.pyenv/versions/3.10.5/lib/python3.10/pdb.py", line 262, in user_line
self.interaction(frame, None)
File "/home/user/.pyenv/versions/3.10.5/lib/python3.10/pdb.py", line 357, in interaction
self._cmdloop()
File "/home/user/.pyenv/versions/3.10.5/lib/python3.10/pdb.py", line 322, in _cmdloop
self.cmdloop()
File "/home/user/.pyenv/versions/3.10.5/lib/python3.10/cmd.py", line 126, in cmdloop
line = input(self.prompt)
ValueError: I/O operation on closed file.
----------------------------------------------------------------------
Ran 16 tests in 3.126s
FAILED (errors=16)
Now that pdb
isn’t in the way anymore, ctrl+c
can stop the tests as well.
Stopping the tests manually has come in handy for me when there are so many tests that it still takes a long time just to print all the failing tests.
Why does this happen?
Every test that hits the breakpoint will break there and open pdb
.
If there are a lot of tests hitting the breakpoint, it will start new pdb
sessions faster than you can press ctrl+c
.
Luckily for me, some weird behavior in the interactive pdb
debugger provides a path to salvation.
I imagine this is how it feels when a deus ex machina lifts someone out of a corner they’ve painted themselves into.