PyCharm

How to Debug in PyCharm Like a Pro

How to Debug in PyCharm Like a Pro

Set a breakpoint by clicking the gutter beside an executable line, then start the program with the Debug icon or Shift+F9 on the default Windows and Linux keymap.

Execution suspends at that line. From there you can step through the code and read variables in the Debug tool window, and expressions can be evaluated against the live frame too.

Plain scripts, Django and Flask servers, pytest runs, and Docker or SSH interpreters all debug the same way, since every session rests on a run/debug configuration.

JetBrains documents debugpy as the default debugger mode for local and WSL interpreters on Python 3.9 and later as of the 2026.2 release, and pydevd stays available as the alternative engine (JetBrains PyCharm documentation, 2026).

How to start a debug session in PyCharm

YouTube player

Click the Run icon in the gutter next to a script’s entry point and choose Debug. PyCharm launches the program under the debugger with a temporary run configuration, and a simple script needs nothing else.

Every session rests on a run/debug configuration, so the launch route only decides how that configuration gets created or picked. The gutter icon builds a temporary one that you can save later. If a configuration already exists, select it in the Run widget and click Debug or press Shift+F9. In the editor, Alt+Shift+F9 opens a menu of configurations to pick from. To adjust one quickly, put the caret on the main declaration, press Alt+Enter and select Edit.

Anything that already runs from PyCharm debugs with the same settings, so running a script normally in PyCharm is the only prerequisite, according to the PyCharm 2026.2 help.

The configuration also fixes the interpreter. If the project has none yet, add a Python interpreter before the first session.

A first session goes like this.

  1. Click the gutter next to an executable line to set a breakpoint.
  2. Click the Run icon beside the main clause and choose Debug.
  3. Type your input in the Console tab of the Debug tool window until the program reaches the breakpoint.
  4. Read the values on the Threads & Variables tab.
  5. Click Step Over to run the line, or Resume (F9) to run to the next breakpoint.

To restart a finished or stuck session, click Rerun or press Ctrl+F5. Stop ends it. Press Ctrl+F2 and, if several processes are running, pick the one to terminate.

Why is Python conquering the tech world?

Explore comprehensive Python statistics: market dominance, developer preferences, salary insights, and growth trends across industries.

Discover Python Insights →

Which breakpoint types does the PyCharm debugger support?

The Breakpoints page of the PyCharm 2026.2 help covers line breakpoints and exception breakpoints. Conditions, logging and dependencies are properties layered on top of them.

Line and conditional breakpoints

A line breakpoint suspends the program just before the marked line runs. Click the gutter on any executable line, or put the caret there and press Ctrl+F8.

For a conditional one, right-click the line number and choose Add Conditional Breakpoint. The condition is a Python Boolean expression, valid at that line.

When the debugger cannot suspend the program at a breakpoint, it marks that breakpoint invalid. This often happens because the line holds no executable code.

PyCharm evaluates the condition every time execution reaches the line. Put one on a hot loop and it runs on every pass, which is where a conditional breakpoint starts to cost real time.

New to Python or just need a quick reference? List comprehensions, built-in functions, and core syntax - including lambda, dict methods, and file I/O - is on one page in the Python Cheat Sheet.

Logging breakpoints

JetBrains recommends non-suspending logging breakpoints over scattered print statements, because the log messages live in one place and the source stays untouched. I’d say that advice holds up.

Right-click the line number and choose Add Logging Breakpoint. For a quick one, Shift+click the gutter and PyCharm logs a line such as “Breakpoint reached: threads.py:28”.

Select an expression in the editor before the Shift+click and PyCharm logs that expression instead.

Exception breakpoints

Exception breakpoints stop the program where the error happens, while the surrounding data still exists. A stack trace only shows what was left afterwards.

Open Run | View Breakpoints (Ctrl+Shift+F8), press Alt+Insert, and select Python Exception Breakpoint.

The On raise option stops when the exception is thrown, and the process keeps running afterwards. On termination waits until the exception ends the process. Ignore library files skips exceptions thrown inside library code.

Django, Jinja2, JavaScript and Jupyter exception breakpoints are listed as PyCharm Pro features.

Managing, muting and chaining breakpoints

  • The Mute Breakpoints button on the Debug toolbar silences all of them, and the program runs on without ending the session
  • Alt+click a single breakpoint to disable it, which keeps its condition intact
  • Every breakpoint is listed in the Bookmarks tool window (Alt+2)
  • Related ones can be grouped through Move to group in the Breakpoints dialog, and F4 in that dialog jumps to the line

Dependent breakpoints use the Disable until breakpoint is hit property. Point it at a master breakpoint, and set the master to not suspend if it should only trigger the others.

As of PyCharm 2026.2, the properties reference lists Remove once hit only for Django, Jinja2 and JavaScript exception breakpoints. A Python line breakpoint stays in the project until you remove it.

Choosing a type

If a bug shows up only on some loop iterations, use a conditional breakpoint. A crash with an unclear cause calls for an exception breakpoint, with Ignore library files on.

When you only want to watch values go by, a logging breakpoint does the job. A path that is reachable only after a specific earlier event is what dependent breakpoints are for.

Plain line breakpoints cover everything else. Add conditions only when stopping on every pass wastes more time than the condition does.

How to step through code with Step Over, Step Into and Run to Cursor

Step Over (F8) runs the current line and Step Into (F7) enters the call on it. Step Out (Shift+F8) returns to the caller, and Run to Cursor (Alt+F9) runs until the caret line. All of them sit on the Debug tool window toolbar.

ActionShortcut (Windows/Linux)BehaviorUse when
Step OverF8Runs the line, skips method bodiesYou trust the call on the line
Step IntoF7Enters the called methodYou doubt a method’s result
Smart Step IntoShift+F7Lets you pick which call to enterSeveral calls share one line
Step Into My CodeAlt+Shift+F7Skips library codeFramework frames get in the way
Step OutShift+F8Returns to the callerYou finished reading a method
Run to CursorAlt+F9Runs until the caret lineYou want to skip a loop without a breakpoint

The stepping page of the help lists Alt+Shift+F7 for both Step Into My Code and Force Step Into. If one of them does nothing on your machine, check Settings | Keymap, and keep the full list of PyCharm keyboard shortcuts open while you remap.

Force variants and breakpoints

Step Over still stops at breakpoints inside the methods it skips, and Run to Cursor stops at any breakpoint on the way to the caret.

The force versions behave differently. Force Step Over (Alt+Shift+F8) ignores breakpoints in called methods, and Force Run to Cursor (Ctrl+Alt+F9) ignores every breakpoint on the way. Reach for them when a breakpoint inside a helper keeps interrupting a trace you are following.

Keeping library code out of the way

Step Into skips some scripts by default, since you rarely need to debug them. Tune the list under Settings | Build, Execution, Deployment | Debugger | Stepping.

  • Do not step into library scripts
  • Do not step into scripts (your own exclusion list)
  • Always do smart step into (clear it to stop the prompt on multi-call lines)

Step Into My Code switches on and off mid-session without a restart, per the JetBrains stepping documentation. The Debug just my code option under Modify options in a run configuration does the same job for every launch.

How to inspect variables, watches and expressions while paused

When the program suspends at a breakpoint, the Debug tool window shows frames, variables and watches for that moment. Evaluate Expression (Alt+F8) runs any code in the context of the selected frame.

Reading the Debug tool window

Frames hold the call stack. Select one to see its file and locals, or double-click it to open the file in a regular tab.

The Variables tab lists everything visible from the selected frame, so a variable that is missing is out of scope there. Watches are expressions you pinned, and they get re-evaluated at every step.

The Console tab covers program input and output, plus an interactive Python prompt that is on by default (PyCharm 2026.2 help).

Closed the window by accident? Press Alt+5. Values also appear inline next to the code, and the inline view is on by default.

Evaluate Expression and watches

Evaluation comes at different levels, from quick to flexible.

  • The hover tooltip shows a simple value but cannot evaluate method calls
  • Quick Evaluate Expression (Ctrl+Alt+F8) handles a selected expression with method calls
  • The Evaluate dialog (Alt+F8) runs arbitrary code in the current frame, loops and function calls included

In the dialog, press Shift+Enter to expand a multiline fragment and Ctrl+Enter to evaluate it. The result comes from the return statement, or from the last line when there is none.

Evaluated calls can change program state. JetBrains warns that a method with side effects alters the flow, so evaluate with that in mind.

Evaluation and watches only work after a breakpoint hit. A program stopped with the Pause button cannot evaluate expressions, according to the PyCharm help. On some Ubuntu setups Alt+F8 is taken by the system, so remap it.

Add a watch with New Watch on the Watches tab, or drag a selected expression into the Variables pane. Watches belong to the project, so they survive a rerun. For a heavy expression, right-click and choose Pause Watch, then evaluate on demand.

Changing a variable value

Select a variable, press F2 (or choose Set Value), type the new value, and press Enter.

The program continues with that value, which lets you test a fix or force a branch without editing the source.

How to debug Django, Flask and pytest in PyCharm

Launch the framework’s own run configuration with the Debug button, and a breakpoint inside a view, route or test suspends the process when execution reaches it. The frameworks differ only in which configuration fields matter.

Django server

Create the configuration under Run | Edit Configurations | Django Server. The Django plugin is bundled and enabled by default, per the PyCharm 2026.2 help.

Host and Port set where the dev server listens, and Additional options carries options for the django-admin.py utility. Leave No reload cleared and debugging in autoreload mode works.

Breakpoints inside a view fire when a request reaches it, and the browser waits until you resume.

Flask server

The Flask Server template builds the FLASK\_APP variable for you. The target can be a Script (a path to a Python file) or a Module (a module name), and Custom takes a combination such as access\_management.access:app2.

The Flask env field defaults to development. Additional options pass –host and –port to flask run, and the interpreter can be a remote one.

pytest

Click the Run icon next to a test and choose Debug. That is the quickest route.

If you have not set up running pytest in PyCharm yet, do that first, since the Debug option reuses the same test configuration.

The target can be a path to a test file or a module name such as my\_tests. A custom target works too, for example my\_tests.test\_car.TestCar.test\_brake.

The Parameters field under Modify options runs a single parametrized sample, for example 2-2.

One known conflict: the pytest-cov plugin can interfere with the debugger. JetBrains’ fix is adding –no-cov -s to the Additional Arguments field.

How to debug remotely and in Docker, SSH or WSL interpreters

A remote interpreter (SSH or a Docker container) starts with the normal Debug button. The other route is the Python Debug Server, where the remote script connects back to your IDE.

RouteNeedsFits when
SSH interpreterSSH access to the remote machineYou can start the program from PyCharm
Docker interpreterDocker plugin and a Docker daemonThe app runs in a container
Python Debug ServerSSH access, plus a local port the remote host can reachA cron job or shell script launches the process

Docker, SSH and WSL interpreters

For SSH, add the interpreter, upload the project through Tools | Deployment | Upload, then use Debug as usual. The code runs on the remote server.

Docker works from the gutter. Click the Run icon, choose Debug, and the debugger runs inside the container. WSL is covered by the default debugger mode as of PyCharm 2026.2 (see the settings section below).

JetBrains documents one side effect. Every run, debug or test execution starts a new Docker container, so the container ID changes between sessions.

You also cannot install Python packages into a Docker-based project interpreter. Bake dependencies into the image instead.

The payoff is environment parity with production, which is the reason JetBrains gives for using Docker here. The Docker plugin ships enabled in PyCharm Pro. Community Edition users install it by hand.

Python Debug Server and pydevd-pycharm

This route is for when you cannot launch the process yourself.

  1. Open Run | Edit Configurations and add a Python Debug Server configuration.
  2. Enter the Port and the IDE host name of your machine, the address the remote script will call.
  3. Install the matching package on the remote host, for example pip install pydevd-pycharm~=242.20224.428 (JetBrains’ own example). Use your IDE’s build number.
  4. Add pydevd\_pycharm.settrace(‘your-ide-host’, port=12345, stdout\_to\_server=True, stderr\_to\_server=True) to the script.
  5. Click Debug. The Debug window shows “Waiting for process connection…” until the remote script starts and connects.

The code executes remotely and is debugged on your machine. The 2026.2 remote debugging page still builds this flow on pydevd-pycharm.

Path mappings

Local and remote file paths must line up, or breakpoints never fire.

In the Python Debug Server configuration, map the local project path to the remote one. For an SSH interpreter, check that the deployment Root path matches the path in the interpreter settings.

On Windows and macOS, Docker runs inside a virtual machine. Volumes and bind mounts outside the mapped local path are unavailable there, while a Linux host can mount any directory.

Which debugger settings are worth changing?

Debugger mode, Attach to subprocess automatically while debugging, Gevent compatible, and Drop into debugger on failed tests change behavior the most. The rest of the Python debugger page covers niche cases.

Debugger mode

The mode option changed between releases, and the help pages for each version describe it differently.

PyCharm help versionWhat the page listsdebugpy scope
2025.3No mode optionNot mentioned
2026.1pydevd as the core engine, debugpy as an optionPython 3.9+, local only
2026.2debugpy as default, pydevd as the alternativePython 3.9+, local and WSL

For remote setups under 2026.2, JetBrains points to Attach to DAP instead of debugpy mode. If a session behaves differently after an upgrade, check this option first.

Subprocesses and gevent

Attach to subprocess automatically while debugging attaches every child of the debugged process, so breakpoints in subprocess code fire. Gevent compatible makes the debugger work with Gevent-monkeypatched code, and the same support switches on with the GEVENT\_SUPPORT=True environment variable.

Failed tests, run-time types and timeouts

Drop into debugger on failed tests stops PyCharm on every failing test and shows the reason for the failure.

Collect runtime types information for code insight preserves the types of function calls during debugging and hands them to the type checker. A Clear caches button removes what it collected.

Debugger evaluation response timeout sets the longest wait for an evaluation result. Raise it if heavy watches time out.

Why does the PyCharm debugger not stop, run slowly or warn about frozen modules?

Each problem here has a documented cause, and most trace back to a path, a subprocess or a Python version.

SymptomCauseFix
Breakpoint never hit on a remote machineLocal and remote paths differCorrect the path mappings and the deployment Root path
Breakpoint ignored in a child processSubprocess not attachedEnable Attach to subprocess automatically while debugging
Breakpoint missed in gevent codeGevent-monkeypatched codeEnable Gevent compatible or set GEVENT\_SUPPORT=True
Slow sessions on Linux with CPython 3.5 to 3.8Cython speedups not installedClick Install in the popup, then check python-dev and gcc
Frozen modules warningPython 3.11+ freezes core modulesPass -Xfrozen\_modules=off in Interpreter options

Cython speedups and slow sessions

The Cython speedups page says PyCharm’s debugger is written in Python, and that compiled C versions make debugging faster. JetBrains calls them reasonable for large code bases. The Debugger settings page adds that the speedups are available for CPython 2.7 and 3.5 to 3.8.

On Windows the precompiled extensions are bundled, so there is nothing to do. macOS is messier. The Cython speedups page says precompiled extensions are bundled, while the Debugger settings page says to compile them manually, so follow whichever message PyCharm shows after the first session.

On Linux, a popup on the first session offers an Install link, and you need write permission to the directories PyCharm uses.

Build errors show up in practice too. “Python.h: no such file or directory” means the Python headers are missing (install python-dev), and “Command ‘gcc’ failed with exit status 1” means there is no C compiler.

One extension is compiled per interpreter version. The pages do not say whether the speedups apply in debugpy mode.

Frozen modules on Python 3.11 and later

Python 3.11 froze the core modules needed at startup, which made interpreter startup 10 to 15 percent faster, per the CPython release notes.

The pydevd warning that followed says frozen modules may make the debugger miss breakpoints, and it asks you to pass -Xfrozen\_modules=off. In PyCharm, put that flag in the Interpreter options field of the run configuration.

A JetBrains support reply points to YouTrack issue PY-56939 for PyCharm’s own critical warning and reports it fixed in 2022.3. Setting PYDEVD\_DISABLE\_FILE\_VALIDATION=1 hides the validation message without changing what the debugger can do.

When the debugger is the wrong tool

Tracing changes timing. Race conditions and timing-sensitive code can behave differently under any debugger, and no PyCharm setting removes that.

Should you use the PyCharm debugger, pdb or breakpoint()?

Use the PyCharm debugger when you work in the IDE and want panes, inline values and watches. Use pdb when you only have a terminal, such as a bare server.

What pdb and breakpoint() offer

The Python docs describe pdb as an interactive source-level debugger with conditional breakpoints, single stepping, stack frame inspection and post-mortem debugging.

The built-in breakpoint() arrived in Python 3.7 through PEP 553. By default it calls pdb.set\_trace(), and the PYTHONBREAKPOINT environment variable redirects it, with PYTHONBREAKPOINT=0 disabling every call.

PEP 553 names IDE integration as a use case for that variable. The PyCharm 2026.2 pages reviewed here do not document how PyCharm handles breakpoint(), so test it with a short script under Debug and under Run.

Pros and cons

The PyCharm debugger keeps variables, frames and watches visible at once. It runs the same way on local, SSH and Docker interpreters, and setting a breakpoint needs no edits to the source. The downsides are that it needs the IDE, remote work needs setup, and Docker interpreters start a new container per execution.

pdb has a different profile.

  • It ships with Python and runs anywhere
  • Post-mortem mode (pdb.pm()) opens a crashed session
  • Python 3.14 adds python -m pdb -p PID to attach to a running process

The catch is text commands only, with no inline values or watch panes.

The obvious choice is the IDE, and it is the right one for most work. A one-line check on a machine with no IDE rarely justifies the Python Debug Server setup, so pdb wins there.

PyCharm Debugger FAQ

How do I view a pandas DataFrame while debugging in PyCharm?

Click View as DataFrame next to the variable in the Variables tab. The data opens in the Data View tool window as a table.

The same list offers View as Array and View as Series for other data structures.

Can I pause a running program without a breakpoint?

Yes. Click the Pause button on the Debug tool window toolbar, and click Resume (F9) to continue.

The Frames pane marks threads stopped this way as frozen, which separates them from threads suspended at a breakpoint.

How do I attach the debugger to a process that is already running?

Choose Run | Attach to Process (Ctrl+Alt+F5) and pick the process from the list. It works for scripts started from the operating system or the PyCharm terminal, as long as they were not started in debug mode.

The list shows processes whose names contain “python” by default, and you can change that pattern in the Python debugger settings. JetBrains’ help describes a “ptrace: Operation not permitted” error on Ubuntu, fixed by lowering kernel.yama.ptrace\_scope.

Attach to Process uses the pydevd backend regardless of the debugger mode you selected. It also cannot attach to processes in Docker, WSL or on a remote host. Use Attach to DAP for those.

Can I debug a Jupyter notebook in PyCharm?

Yes. Set breakpoints in a cell, then click Debug Cell in the cell toolbar or right-click the cell and choose it. PyCharm covers local and remote Jupyter server kernels.

As of the 2026.2 help, notebook debugging uses debugpy by default, with pydevd also supported, except for remote Jupyter notebooks, which debugpy does not yet cover. Jupyter support is part of PyCharm’s free core in the unified product, so debugging a notebook does not need a Pro subscription. Remote development support for Jupyter is a Pro feature.

Does the PyCharm debugger work with asyncio code?

Partly. JetBrains documents that the Debug Console can run asyncio coroutines, and the Python console can execute a selected coroutine definition.

The Frames pane lists threads, and the documentation I found does not describe switching between coroutine stacks. A JetBrains community thread asks for that feature.

When a Bug Disappears Under the PyCharm Debugger

A bug that vanishes under the PyCharm debugger calls for a different way of observing it, not more stepping. Swap suspending breakpoints for non-suspending logging breakpoints and read the console after a normal run.

The order of escalation follows how much each step interrupts the program.

  • Logging breakpoints on the suspect lines
  • Attach to Process after a normal start
  • An exception breakpoint set to On termination

Logging never stops the program. Attaching skips the debugger during startup, and the exception breakpoint pauses only at the crash.

The trade-off is foresight. A logging breakpoint records only the expressions you chose in advance, so a wrong guess costs one more run.

Once the cause is found, pin the fix with a regression test so the fault cannot return unnoticed.

This sequence follows the PyCharm 2026.2 help, checked on October 7, 2026. A change to the default debugger mode is the reason to recheck it.

Bogdan Sandu

Stay sharp. Ship better code.

Every week: one curated article, one tool worth knowing, one tip you can use tomorrow. No noise, no padding.