Python Debuggers: Tools and How to Use Them
Learn how to debug Python with pdb, VS Code, and PyCharm: set breakpoints, inspect values, step through code, attach to processes, and fix common problems.
A Python debugger pauses your program so you can inspect its current state, follow the call stack, and advance execution one step at a time. For a quick terminal session, use the standard-library pdb; in an editor, use the Python Debugger in VS Code or Debug mode in PyCharm. The basic workflow is the same: choose where execution stops, launch or attach the debugger, inspect values, step through the code, then continue or end the session.
This guide covers the three options, runnable examples, launch and attach workflows, and common failure cases. Which tool fits depends on your editor, interpreter, framework, and whether you need to debug a running or remote process.
1. What a Python debugger does
A debugger gives you a controlled view of a program while it runs. Instead of adding print statements and rerunning the program, you can pause at a line and inspect local variables, evaluate expressions in the current frame, move through the call stack, and resume execution. A breakpoint marks a place where execution should pause.
Debuggers commonly support these actions:
- Continue: resume until the next breakpoint or the program ends.
- Step over: run the current line without entering a function call.
- Step into: enter the function called on the current line.
- Step out: finish the current function and return to its caller.
- Inspect: evaluate expressions and view variables in the current frame.
- Examine the stack: see how execution reached the current line.
In pdb, these are text commands. VS Code and PyCharm provide graphical controls and variable panes. The underlying questions are the same: where did execution stop, what values are present, and how did the program get here?
2. Choose a debugger
| Situation | Good starting point | Check first |
|---|---|---|
| Small script, terminal session, or quick investigation of an exception | pdb |
Use the Python interpreter that runs the script. Process attachment with -p requires Python 3.14 or later. |
| Project already open in VS Code | Python Debugger extension | Confirm the selected interpreter and whether you need a launch or attach configuration. |
| Project already open in PyCharm | PyCharm Debug mode | Check the debugger mode and whether your interpreter, framework, and process arrangement are covered. |
| Remote or already-running process | Compare each debugger’s attach workflow | Confirm network security, interpreter location, subprocess behavior, and framework support. |
For a one-off local script, start with the tool already available in your workflow. For specialized cases—remote targets, subprocesses, notebooks, or framework-managed commands—check the tool’s current compatibility documentation before building a workflow around it. The official Python pdb reference, VS Code Python debugging guide, and PyCharm debugger documentation describe their supported paths.
3. Debug with Python’s built-in pdb
pdb is an interactive source debugger included in Python’s standard library. It supports breakpoints, conditional breakpoints, line stepping, stack inspection, source listing, expression evaluation in a frame, and post-mortem debugging.
Option A: pause with breakpoint()
Add breakpoint() where you want execution to stop, then run the script with the interpreter you normally use:
# debug_me.py
def total_with_tax(prices, rate):
subtotal = sum(prices)
breakpoint() # Execution enters pdb here.
return subtotal * (1 + rate)
print(total_with_tax([12.50, 8.25], 0.08))
python debug_me.py
At the (Pdb) prompt, try:
p subtotal # Print an expression in the current frame
p rate
where # Show the stack; w is a shortcut
list # Show nearby source lines
n # Next: run this line without entering a called function
s # Step: enter a function called on this line
c # Continue until another breakpoint or program exit
q # Quit the debugging session
In this example, subtotal is available because the debugger stopped inside total_with_tax, after that local variable was assigned. If you inspect a name before it is defined, Python reports an error; move to the assignment or inspect a value that already exists.
Option B: launch the script under pdb
Run the script under debugger control without adding a breakpoint to its source:
python -m pdb debug_me.py
The debugger starts before the script runs. Use n or s to advance, or enter b 4 to set a breakpoint at line 4, then c to run to it. Use help or help COMMAND at the prompt for command details.
Useful pdb commands
| Command | What it does |
|---|---|
p expression |
Evaluate and print an expression in the current frame. |
pp expression |
Pretty-print an expression. |
b location |
Set a breakpoint at a line or function location. |
b location, condition |
Set a conditional breakpoint. For example: b 12, count > 10. Omit the leading space when typing the command. |
cl |
Clear breakpoints; follow the prompt or provide a breakpoint number. |
where or w |
Show the stack trace and current frame. |
up / down |
Move to a caller or callee frame for inspection. |
list or l |
List source around the current location. |
next or n |
Run the current line, stepping over function calls. |
step or s |
Run the current line and enter a function call. |
return or r |
Run until the current function returns. |
continue or c |
Resume until another stop or program exit. |
q |
Quit the debugger. |
For a conditional breakpoint, use the syntax documented by pdb, such as b 12, count > 10. The debugger evaluates the condition in the relevant frame when execution reaches the breakpoint.
Investigate an exception after it happens
When a program exits abnormally under pdb, the debugger can enter post-mortem mode so you can inspect the frames and values that led to the exception. You can also call pdb.pm() after an exception has been handled to examine the last traceback:
try:
result = 10 / 0
except ZeroDivisionError:
import pdb
pdb.pm()
At the prompt, use where to see the traceback and up, down, and p to inspect frames and expressions.
Attach to a process with pdb (Python 3.14+)
Python 3.14 documents attaching to a running process by PID:
python -m pdb -p 12345
Replace 12345 with the process ID. This feature was added in Python 3.14, so it is not available in older Python releases. A process blocked in a system call or waiting for I/O may not stop immediately; the documentation notes it may need to execute another bytecode instruction or receive a signal before attachment takes effect. See the pdb documentation for version-specific behavior.
4. Debug with VS Code and debugpy
VS Code’s Python Debugger supports a quick current-file workflow and configurable launch or attach sessions. The selected workspace interpreter is used by default; verify that it is the same environment where the project’s dependencies are installed.
Debug the open file
- Install the Microsoft Python and Python Debugger extensions if they are not already available.
- Open the Python file and select the intended interpreter in VS Code.
- Set a breakpoint by clicking beside a source line.
- Choose Python Debugger: Debug Python File from the run/debug control.
- When execution pauses, inspect Variables, Watch, and Call Stack; use the debug toolbar to step, continue, or stop.
For a repeatable project configuration, create a Python debugger configuration in .vscode/launch.json. For example, a basic file launch can look like this:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
}
]
}
Choose the Python File configuration and press F5 to start debugging. VS Code’s configuration options also let you specify a module, arguments, environment, working directory, or a different interpreter when the defaults do not match the project.
Attach to an existing process
VS Code distinguishes launching a program from attaching to one that is already running. Choose an attach configuration when the target process must be started separately; an attach-by-process-ID configuration is available for supported local processes. Confirm the target interpreter and process setup in the VS Code debugging guide.
Use debugpy from the command line
Install debugpy in the environment used by the target program:
python -m pip install --upgrade debugpy
Start a script with a debug listener on the loopback interface:
python -m debugpy --listen 127.0.0.1:5678 debug_me.py
For remote debugging, VS Code documents starting debugpy on the target and attaching from the local editor. Restrict access to the debug listener; use a secure connection such as SSH when appropriate, and do not expose an unauthenticated debug port to an untrusted network. Follow the documented remote setup rather than treating the loopback example above as a complete remote configuration.
5. Debug with PyCharm
In PyCharm, set a line breakpoint, choose the project’s run configuration, and start it with Debug. When the program pauses, inspect variables and the call stack in the Debug tool window, then step, continue, or stop from the debugger controls.
- Open the project and select the intended Python interpreter.
- Click in the gutter beside a source line to set a breakpoint.
- Select or create the run configuration that represents the script or application.
- Start the configuration in Debug mode.
- Inspect the suspended state, evaluate expressions, and step through relevant lines.
JetBrains documents debugpy as the default debugger for Python 3.9 or later on local and WSL interpreters, with pydevd as an alternative. Its documentation identifies scenarios not yet covered by debugpy, including some remote targets, attach-to-process workflows, Sphinx doctest, Scrapy, remote Jupyter notebooks, and certain manage.py tasks. Remote DAP attachment and alternative debugger selection have separate documented paths. Check the current debugger settings and debug workflow documentation for your specific interpreter and framework.
6. Work through a debugging session
These steps apply whether you use a terminal debugger or an IDE.
- Reproduce the issue. Write down the input and conditions that trigger it. A debugger is most useful when the failure can be repeated.
- Choose a useful stop point. Place a breakpoint before the incorrect result is produced, not only at the final line where it becomes visible.
- Run or attach. Launch the program under debugger control for a new session; attach when the target is already running and the debugger supports that setup.
- Check the current frame. Inspect inputs, local variables, and relevant object fields. In
pdb, usewhereandp expression; in an IDE, use the variable and call-stack panes. - Step with a question in mind. Step into a call to inspect its internals, step over it when its implementation is not relevant, or step out to return to the caller.
- Confirm the cause. Follow the values to the first point where actual behavior diverges from expected behavior.
- Fix and rerun. Verify the corrected code with the same reproducing input. Remove temporary breakpoints or
breakpoint()calls before committing if they are no longer needed.
Prefer a narrow breakpoint or a conditional breakpoint in a loop over stopping on every iteration. This keeps the session focused and makes it easier to see the state that matters.
7. Common errors and fixes
| Symptom | Likely cause | What to do |
|---|---|---|
breakpoint() does not enter pdb |
The program is using a non-default breakpoint hook, or PYTHONBREAKPOINT disables the built-in hook. |
Check the environment and breakpoint configuration. To launch directly under the standard debugger, use python -m pdb path/to/script.py. |
pdb says a variable is undefined |
The current frame has not assigned that name, or you are inspecting a different stack frame. | Use where and up/down to select the right frame; step past the assignment before inspecting the local. |
| A breakpoint is never hit | The line is not executed, the wrong file or interpreter is running, or the breakpoint location does not match loaded source. | Confirm the code path and launch target; inspect the selected interpreter and working directory; set the breakpoint on an executable line. |
| VS Code cannot find a package | The debugger launched a different environment from the one where the package is installed. | Select the project interpreter, then install dependencies using that interpreter’s python -m pip. |
| VS Code starts the wrong program or arguments | The launch configuration points to a different file, module, working directory, or argument list. | Review launch.json, especially program or module, args, and cwd. |
| debugpy attach cannot connect | The target is not listening at the expected endpoint, the port is unreachable, or the listener is bound to a different interface. | Check the target command, address, port, firewall, and attach configuration. Keep the listener restricted and use a secure tunnel for remote access. |
| pdb process attachment appears stalled | The target may be blocked in a system call or waiting for I/O. | On Python 3.14+, consult the documented attach behavior; attachment may take effect after another bytecode instruction or a signal. |
| PyCharm debugging behaves differently for a framework or remote target | The selected debugger may not cover that workflow. | Check JetBrains’ current coverage notes, debugger mode, interpreter type, and documented alternative or DAP path. |
| Stepping seems to skip lines | One source line can execute multiple operations, or the debugger is stepping over a call. | Use step into when you need to inspect a called function, and inspect the exact expression and frame rather than relying on visual line count. |
8. Performance, reliability, and workflow notes
- Expect pauses to affect timing. A breakpoint stops the target. Timing-sensitive behavior can change while you inspect it, so use logs or a purpose-built reproducer alongside the debugger when timing itself is part of the problem.
- Keep the target environment consistent. Launch with the interpreter, dependencies, environment variables, and working directory that reproduce the issue. Many apparent debugger problems are mismatched launch settings.
- Use conditional stops for noisy loops. A condition can avoid repeatedly pausing on iterations that are not relevant.
- Plan for child processes. A debugger attached to a parent process may not automatically cover every subprocess or framework worker. Check the debugger’s configuration and the framework’s execution model.
- Secure remote debugging. A debug listener grants control over the target process. Restrict network access and use a secure connection such as SSH where appropriate.
- Check version-specific features. For example, the
pdb -pprocess-attach option is documented for Python 3.14 and later. Do not assume it exists in older interpreters.
There is no universal winner or speed ranking supported by these sources. Choose based on whether you prefer a console or GUI, need launch or attach, and the Python version, interpreter location, remote setup, subprocess behavior, and framework involved.
9. Or skip the browser setup
Debuggers help inspect Python code. If the task also involves capturing a web page for a test, report, or AI workflow, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF. See the ScreenshotNeo API docs for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use the screenshot tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.
10. Frequently asked questions
Is pdb installed separately?
No. pdb is part of Python’s standard library.
Can I debug code without changing the source?
Yes. Start a script with python -m pdb path/to/script.py, or use an IDE launch configuration and set breakpoints in the editor.
Can I attach pdb to any Python version?
The documented python -m pdb -p PID attachment option requires Python 3.14 or later. Other debugger attach workflows have their own requirements.
Should I always step into a function?
No. Step into when the function’s behavior may explain the issue; step over when you only need its result and want to stay focused on the caller.
Which debugger should a beginner use?
Use the tool that matches the environment you already run: pdb for a terminal-first script, or your editor’s debugger when you want a graphical view. For remote or framework-specific work, verify support before choosing.


