I use PySnooper[1] when code behavior deviates inscrutably from my mental model. Like Icecream, PySnooper exhorts, "Never use print for debugging again." A simple example:
import pysnooper
@pysnooper.snoop()
def add_up(numbers):
total = 0
for number in numbers:
total += number
return total
add_up([123, 456])
When run, PySnooper prints the activity of the decorated function or method: $ python example.py
Source path:... /home/joel/example.py
Starting var:.. numbers = [123, 456]
08:46:58.742543 call 4 def add_up(numbers):
08:46:58.742692 line 5 total = 0
New var:....... total = 0
08:46:58.742722 line 6 for number in numbers:
New var:....... number = 123
08:46:58.742755 line 7 total += number
Modified var:.. total = 123
08:46:58.742787 line 6 for number in numbers:
Modified var:.. number = 456
08:46:58.742818 line 7 total += number
Modified var:.. total = 579
08:46:58.742847 line 6 for number in numbers:
08:46:58.742876 line 8 return total
08:46:58.742898 return 8 return total
Return value:.. 579
Elapsed time: 00:00:00.000405
[1] https://github.com/cool-RR/PySnooper