Python Debugging Handbook – How to Debug Your Python Code
Python Debugging Handbook – How to Debug Your Python Code
Table of Contents
1. Common Code Error Messages
9. TypeError
10. ValueError
14. Logging
16. Assertions
17. ** [Advanced Debugging Techniques**](#heading-advanced-
Forum Donate
debugging-techniques)
Learn to code — free 3,000-hour curriculum
18. Unit Testing
23. Profiling
If you're already familiar with common code error messages, feel free to skip
this section and move straight to the Debugging Techniques.
2. IndentationError: unexpected
indent
Python relies on indentation to define code blocks. This error occurs
when indentation is inconsistent or incorrect.
Make sure to check for typos in variable or function names, and make
sure they are defined before use.
To fix this, review the code and confirm that the attribute or method
being called is correct and available.
Forum Donate
5. FileNotFoundError: [Errno 2] No
Learn to code — free 3,000-hour curriculum
You should check the file path and make sure the file exists at the
specified location.
To fix it, make sure that the index being used is within the valid range
of the sequence.
8. TypeError:
This is a common exception in Python that occurs when an operation
or function is applied to an object of an inappropriate type. Here are
some common types of TypeError : Forum Donate
9. ValueError:
This type of error occurs when a function receives an argument of the
correct type but with an inappropriate value.
Foundational Debugging
Techniques:
Print Statements
When you're writing code, especially in complex programs, it's
Forum Donate
essential to understand how your code is executing and the values of
Learn to code — free 3,000-hour curriculum
variables at different points in the program. Print statements allow
you to insert messages in your code that get printed to the console or
terminal when the program runs.
Here's an example:
Logging
Logging is like writing notes while your program runs. Instead
Forumof just Donate
printing things to the screen, you write them to a log. It helps you keep
Learn to code — free 3,000-hour curriculum
track of what your program is doing, especially when things go wrong.
You can configure logging to control the level of detail in log messages
and specify where the logs should go. This could be the console, a file,
or other destinations.
Logging Levels:
DEBUG: Detailed information, useful for developers during
debugging.
import logging
logging.basicConfig(level=logging.Info)
# Optional
logging.basicConfig(
filename='example.log',
format='%(asctime)s - %(levelname)s - %(message)s',
level=logging.DEBUG
)
# this format includes the timestamp, module name, and log level in
Note : In larger applications, it's common to use loggers Forum
instead of theDonate
root logger directly. This approach allows for more granular control
Learn to code — free 3,000-hour curriculum
over logging in different parts of the application. You can read more
about logging and loggers here.
To learn more about logging and loggers in Python, check out this blog:
https://www.samyakinfo.tech/blog/logging-in-python
Exception Handling
Wrap suspicious code blocks with try-except statements to catch and
handle exceptions. This prevents your program from crashing
abruptly, allowing you to gracefully handle errors and log relevant
information.
try:
result = x / y
except ExceptionType as e:
print(f"An exception of type {type(e).__name__} occurred: {e}")
# Additional handling logic, if needed
# Eg.
safe_divide(10, 2)
safe_divide(5, 0)
Assertions
An assertion is a statement that you add to your code to check if a
certain condition holds true. If the condition is false, it indicates a bug
or an unexpected situation in your program.
# Handling AssertionError
try:
assert x > 0, "x should be greater than zero"
except AssertionError as e:
print(f"Assertion failed: {e}")
# my_module.py
class TestAddNumbers(unittest.TestCase):
def test_add_numbers(self):
result = add_numbers(2, 3)
self.assertEqual(result, 5)
if __name__ == '__main__':
unittest.main()
Using the same example as before, a pytest test might look like this:
# test_my_module.py
def test_add_numbers():
result = add_numbers(2, 3)
assert result == 5
To run the tests, simply execute: Forum Donate
pytest test_my_module.py
While print statements and logging are helpful for basic debugging,
PDB takes debugging to the next level by allowing you to intervene
and analyze your code in real-time.
In your Python script, you start by importing the pdb module. This
module provides the functionality for debugging Python code. import
pdb
import pdb
Setting breakpoints
To start debugging at a specific point in your code, you insert the
pdb.set_trace() statement. This line acts as a breakpoint, indicating
where the debugger should pause the execution of the program.
Forum Donate
When the program reaches this line during execution, it will pause, and
the debugger will be activated.
You can set breakpoints directly in your code using the break
command. For example:
import pdb
def some_function():
# Setting a breakpoint at line 4
pdb.breakpoint()
print("Hello, World!")
some_function()
Basic Commands:
Now, you can interact with the debugger and use various commands
to inspect variables, step through the code, and identify and fix issues.
Command Functionality
list (or l) list or list [first[, last]]: Display the source code around the curren
print (or p) print expression: Evaluate and print the value of the specified exp
Debugger Extensions
Consider using third-party debugging tools and extensions, such as
pdbpp , pudb and ipdb , which enhance the functionality of the built-
in PDB debugger.
pdbpp provides additional features such as syntax highlighting,
Forum tab- Donate
completion, and better navigation capabilities.
Learn to code — free 3,000-hour curriculum
To use any of them, replace pdb with the corresponding debugger you
want to use. For eg. import pdb; pdb.set_trace() with import
pdbpp; pdbpp.set_trace() in your code.
Remote Debugging
Remote debugging refers to the process of debugging code that is
running on a system or server separate from the development
environment. This is commonly used when the application is deployed
on a remote server, in the cloud, or on a different device.
Visual Breakpoints:
Breakpoints are markers that pause the execution of your Python
program at a specific line of code, allowing you to inspect variables,
evaluate expressions, and understand the flow of your program at that
point.
Step Into (F7): Moves to the next line of code and enters
function calls if applicable.
Step Over (F8): Executes the current line of code and stops at
the next line, skipping function calls.
Debuggers in IDEs allow you to execute your code step by step. This
includes stepping into functions, stepping over lines, and stepping out
of functions. This fine-grained control helps you trace the flow of yourDonate
Forum
program and identify the exact location of an issue.
Learn to code — free 3,000-hour curriculum
PyCharm, for instance, displays the call stack in the debugger tool
window.
Variable Inspection:
Inspecting variables is crucial for understanding how data changes
during program execution. IDEs provide a Variables panel where you
can view the current state of variables, making it easier to identify
bugs. Simply hover over a variable or check the Variables tab to see its
current value.
Conditional Breakpoints:
In addition to standard breakpoints, some IDEs allow you to set
breakpoints with conditions. This means the debugger will only pause
if a specified condition is met. This can be helpful when you want to
investigate a specific scenario or condition in your code.
Watch Expressions:
Watch expressions allow you to monitor specific variables or
expressions continuously as your program runs. This feature is
beneficial when you want to keep an eye on certain values without
manually inspecting them at each breakpoint.
PyCharm: Add expressions to the "Watches" pane toForum
monitor Donate
them throughout debugging.
Learn to code — free 3,000-hour curriculum
Visual Studio Code: Use the "Watch" pane to add expressions
for continuous monitoring.
There are more tools that IDE provides for debugging purposes like:
Performance Debugging:
Code Linters and Analyzers
Code linters and static analyzers are tools that help identify potential
issues in your code by analyzing the source code without executing it.
They can catch common programming errors, enforce coding
standards, and provide valuable suggestions for improvement.
Here, we'll talk about a couple of these tools – PyLint and mypy – so
you can see how to install them and how they work.
How to install PyLint: Forum Donate
pylint your_script.py
PyLint can be customized to fit your project's specific needs. You can
create a configuration file (usually named .pylintrc ) to define your
preferences. This file can be placed in the root of your project. eg:
[MASTER]
enable = all
[MESSAGES CONTROL]
disable = missing-docstring
In this example, we enable all checks except for the missing docstring
check. You can tailor the configuration to match your coding style and
project requirements.
How to install mypy: Forum Donate
mypy your_script.py
Mypy will check your code for type-related issues and provide
feedback on potential type mismatches and violations of type
annotations.
flake8
flake8 combines three main tools:
Similar to PyLint, you can run flake8 on your Python code by executing
the following command in your terminal:
flake8 your_file.py
#Replace your_file.py with the actual name of your Python file.
[flake8]
max-line-length = 88
extend-ignore = E203, W503
Black
Black is an opinionated code formatter that automates formatting
Forum Donate
decisions for consistent and readable code.
Learn to code — free 3,000-hour curriculum
black your_file.py
Black complements traditional linters like PyLint and flake8. You can
use these tools in combination to ensure both code quality and
consistent formatting.
Many popular editors like Visual Studio Code, Atom, and Sublime Text
have extensions or plugins that allow you to use these Code Linters
and Analyzers results directly within the editor as you write code.
Profiling
Profiling involves analyzing the performance of your code to identify
bottlenecks and areas that can be optimized. Python provides built-in
tools and external libraries for profiling, helping developers gain
insights into their code's execution time and resource usage.
Identify Performance Issues: Profiling allows you toForum
pinpoint Donate
sections of your code that consume the most time and
Learn to code — free 3,000-hour curriculum
resources, aiding in optimization efforts.
Python comes with built-in modules for basic profiling. The two main
modules are cProfile and profile .
1. cProfile:**
cProfile is a built-in module that provides a deterministic profiling of
Python programs. It records the time each function takes to execute,
making it easier to identify performance bottlenecks.
Example:
import cProfile
def example_function():
# Your code here
if __name__ == "__main__":
cProfile.run('example_function()')
import profile
def example_function():
# Your code here
if __name__ == "__main__":
profile.run('example_function()')
Both cProfile and profile produce similar outputs, but the former
is generally preferred for its lower overhead.
Installing snakeviz:
Using snakeviz:
import cProfile
Forum Donate
import snakeviz
Learn to code — free 3,000-hour curriculum
def example_function():
# Your code here
if __name__ == "__main__":
cProfile.run('example_function()', 'profile_results')
snakeviz.view('profile_results')
Line Profiling:
Line profiling allows you to see how much time is spent on each line of
code within a function. The line_profiler module is commonly used
for this purpose.
Installing line_profiler:
Using line_profiler:
from line_profiler import LineProfiler
Forum Donate
if __name__ == "__main__":
profiler = LineProfiler()
profiler.add_function(example_function)
profiler.run('example_function()')
This will show a detailed report with the time spent on each line within
the example_function .
Memory Profiling:
Understanding memory usage is crucial for optimizing code. The
memory_profiler module helps in profiling memory consumption.
Installing memory_profiler:
Using memory_profiler:
@profile
def example_function():
# Your code here
if __name__ == "__main__":
example_function()
Forum Donate
These are the ways how you can find the solution for your problem.
Even if you don’t find an exact match to your problem, similar issues
might provide insights into potential solutions.
If you can't find a solution at all, consider posting your problem
Forum on Donate
relevant forums or communities. Others may have faced similar issues
Learn to code — free 3,000-hour curriculum
and can offer assistance.
Conclusion
In this debugging handbook, we've explored common error messages,
learned effective search strategies, and discovered the practical utility
of print statements.
Samyak Jain
Read more posts.
Our mission: to help people learn to code for free. We accomplish this by creating thousands of
videos, articles, and interactive coding lessons - all freely available to the public.
Donations to freeCodeCamp go toward our education initiatives, and help pay for servers,
services, and staff.
Mobile App
Forum Donate
Our Charity
About Alumni Network Open Source Shop Support Sponsors Academic Honesty