162 lines
3.5 KiB
Markdown
162 lines
3.5 KiB
Markdown
[Contents](../Contents.md) \| [Previous (8.2 Logging)](02_Logging) \| [Next (9 Packages)](../09_Packages/00_Overview)
|
|
|
|
# 8.3 Debugging
|
|
|
|
### Debugging Tips
|
|
|
|
So, you're program has crashed...
|
|
|
|
```bash
|
|
bash % python3 blah.py
|
|
Traceback (most recent call last):
|
|
File "blah.py", line 13, in ?
|
|
foo()
|
|
File "blah.py", line 10, in foo
|
|
bar()
|
|
File "blah.py", line 7, in bar
|
|
spam()
|
|
File "blah.py", 4, in spam
|
|
line x.append(3)
|
|
AttributeError: 'int' object has no attribute 'append'
|
|
```
|
|
|
|
Now what?!
|
|
|
|
### Reading Tracebacks
|
|
|
|
The last line is the specific cause of the crash.
|
|
|
|
```bash
|
|
bash % python3 blah.py
|
|
Traceback (most recent call last):
|
|
File "blah.py", line 13, in ?
|
|
foo()
|
|
File "blah.py", line 10, in foo
|
|
bar()
|
|
File "blah.py", line 7, in bar
|
|
spam()
|
|
File "blah.py", 4, in spam
|
|
line x.append(3)
|
|
# Cause of the crash
|
|
AttributeError: 'int' object has no attribute 'append'
|
|
```
|
|
|
|
However, it's not always easy to read or understand.
|
|
|
|
*PRO TIP: Paste the whole traceback into Google.*
|
|
|
|
### Using the REPL
|
|
|
|
Use the option `-i` to keep Python alive when executing a script.
|
|
|
|
```bash
|
|
bash % python3 -i blah.py
|
|
Traceback (most recent call last):
|
|
File "blah.py", line 13, in ?
|
|
foo()
|
|
File "blah.py", line 10, in foo
|
|
bar()
|
|
File "blah.py", line 7, in bar
|
|
spam()
|
|
File "blah.py", 4, in spam
|
|
line x.append(3)
|
|
AttributeError: 'int' object has no attribute 'append'
|
|
>>>
|
|
```
|
|
|
|
It preserves the interpreter state. That means that you can go poking
|
|
around after the crash. Checking variable values and other state.
|
|
|
|
### Debugging with Print
|
|
|
|
`print()` debugging is quite common.
|
|
|
|
*Tip: Make sure you use `repr()`*
|
|
|
|
```python
|
|
def spam(x):
|
|
print('DEBUG:', repr(x))
|
|
...
|
|
```
|
|
|
|
`repr()` shows you an accurate representation of a value. Not the *nice* printing output.
|
|
|
|
```python
|
|
>>> from decimal import Decimal
|
|
>>> x = Decimal('3.4')
|
|
# NO `repr`
|
|
>>> print(x)
|
|
3.4
|
|
# WITH `repr`
|
|
>>> print(repr(x))
|
|
Decimal('3.4')
|
|
>>>
|
|
```
|
|
|
|
### The Python Debugger
|
|
|
|
You can manually launch the debugger inside a program.
|
|
|
|
```python
|
|
def some_function():
|
|
...
|
|
breakpoint() # Enter the debugger (Python 3.7+)
|
|
...
|
|
```
|
|
|
|
This starts the debugger at the `breakpoint()` call.
|
|
|
|
In earlier Python versions, you did this. You'll sometimes see this
|
|
mentioned in other debugging guides.
|
|
|
|
```python
|
|
import pdb
|
|
...
|
|
pdb.set_trace() # Instead of `breakpoint()`
|
|
...
|
|
```
|
|
|
|
### Run under debugger
|
|
|
|
You can also run an entire program under debugger.
|
|
|
|
```bash
|
|
bash % python3 -m pdb someprogram.py
|
|
```
|
|
|
|
It will automatically enter the debugger before the first
|
|
statement. Allowing you to set breakpoints and change the
|
|
configuration.
|
|
|
|
Common debugger commands:
|
|
|
|
```code
|
|
(Pdb) help # Get help
|
|
(Pdb) w(here) # Print stack trace
|
|
(Pdb) d(own) # Move down one stack level
|
|
(Pdb) u(p) # Move up one stack level
|
|
(Pdb) b(reak) loc # Set a breakpoint
|
|
(Pdb) s(tep) # Execute one instruction
|
|
(Pdb) c(ontinue) # Continue execution
|
|
(Pdb) l(ist) # List source code
|
|
(Pdb) a(rgs) # Print args of current function
|
|
(Pdb) !statement # Execute statement
|
|
```
|
|
|
|
For breakpoints location is one of the following.
|
|
|
|
```code
|
|
(Pdb) b 45 # Line 45 in current file
|
|
(Pdb) b file.py:45 # Line 34 in file.py
|
|
(Pdb) b foo # Function foo() in current file
|
|
(Pdb) b module.foo # Function foo() in a module
|
|
```
|
|
|
|
## Exercises
|
|
|
|
### Exercise 8.4: Bugs? What Bugs?
|
|
|
|
It runs. Ship it!
|
|
|
|
[Contents](../Contents.md) \| [Previous (8.2 Logging)](02_Logging) \| [Next (9 Packages)](../09_Packages/00_Overview)
|