Skip to content

IDE Customization

BetterPy tweaks a handful of PyCharm UI surfaces — the navigation bar, Structure View, Go-to popups, and the run console — without touching your source code.


Python navigation bar

Since 2026.04.11 · Maturity Stable · Issues PY-53757

BetterPy enriches the Python navigation bar with module-path context, making it easier to understand where the current file sits inside a package.

How to use

Open a Python file and show the IDE navigation bar. The enhanced model contributes Python-aware path elements for packages, modules, classes, and functions.

Example

For a file such as:

shop/orders/service.py

with a caret inside:

class CheckoutService:
    def submit(self):
        ...

the navigation bar can expose the project/package path and the Python structure around the caret, so jumping to nearby modules or enclosing declarations takes fewer clicks.

What it supports

  • Python package and module path display.
  • Navigation bar entries for Python source structure.
  • Standard navigation-bar selection and keyboard interaction.
  • Projects with nested source roots and package directories.

What it deliberately avoids

  • Changing file contents or import paths.
  • Replacing PyCharm's project view.
  • Inventing entries for unresolved or non-Python files.

Notes

  • The feature is passive once enabled; use the IDE navigation bar as usual.
  • The feature is gated by the Python navigation bar setting under Settings | BetterPy | IDE Customization | Navigation.

Presentation

Enhanced Go to Implementation presentation

Since 2026.04.12 · Maturity Stable · Issues PY-82520

Prepends the owning class name to the method name in the Go to Implementation popup whenever two or more candidates share the same short name, so overrides can be told apart at a glance.

In a typical Python codebase, multiple classes implement a method with the same name — for example, several services implementing process, or a hierarchy of handlers all overriding handle. When you invoke Navigate → Go to Implementation (⌥⌘B / Ctrl+Alt+B) on such a method, PyCharm shows the Choose implementation popup with one row per candidate.

Out of the box, every row renders only the method name plus the file it lives in:

process  →  module_a.py
process  →  module_b.py
process  →  module_c.py

This creates a concrete pain point: Speed-search cannot narrow the list. The popup's type-to-filter search only matches against the method/class name only, not the FQN. Since every row shows the same method name, typing ServiceA or UserHandler filters out all rows instead of focusing on the override you want.

BetterPy contributes a gotoTargetPresentationProvider for Python that rewrites the row presentation when — and only when — there is a disambiguation problem. For each candidate method whose short name collides with another candidate in the same popup, the presentation is changed to ClassName.methodName, while the file/location column is kept intact:

ServiceA.process  →  module_a.py
ServiceB.process  →  module_b.py
ServiceC.process  →  module_c.py

With the class name inline you can now Use speed-search to narrow down — typing ServiceB filters the list down to the matching row, because the class name is part of the row's searchable text.

Candidates whose short name is already unique in the popup are left untouched, so the presentation stays minimal and only adds information when it's actually useful.

How to invoke

Use the standard IDE action Navigate | Go to Implementation on a Python method with multiple implementation candidates. BetterPy adjusts the popup rows automatically when candidate names would otherwise be ambiguous.

Example

class CsvExporter:
    def process(self) -> None:
        ...


class JsonExporter:
    def process(self) -> None:
        ...

Invoking Go to Implementation on a shared process declaration can show rows such as:

CsvExporter.process  ->  exporters.py
JsonExporter.process ->  exporters.py

Typing JsonExporter in the popup speed-search narrows the list to the JSON implementation.

What it supports

  • Python Go to Implementation popups with duplicate method names.
  • Keeping the file and location columns unchanged.
  • Leaving already unique candidates untouched.
  • Standard IDE keyboard and speed-search interaction.

What it deliberately avoids

  • Changing source code, names, or navigation targets.
  • Adding class names when they are not needed for disambiguation.
  • Replacing the IDE's own implementation search.

Notes

  • The feature is passive once enabled.
  • The feature is gated by the Enhanced Go to Implementation presentation setting under Settings | BetterPy | IDE Customization | Presentation.

Test/Production editor tabs

Since 2026.04.14 · Maturity Incubating

Keeps Python test files in the left editor split and Python production files in the right editor split while the mode is enabled. When navigating (e.g., Go to Declaration) from one test/production file to another same-kind file, the target file is opened in the opposite split to preserve your editing context.

Test-file classification follows the applicable pytest python_files patterns in addition to collected test data and conftest.py, so custom test file names stay in the test split.

How to invoke: Use the Test/Production Split icon in the title toolbar.

The same menu includes Save Editor Layout and Restore Editor Layout. Saving replaces the project's previous snapshot and records the left/right open tabs, focused tab, and its caret position. The versioned snapshot is kept in the project's IDE state (testProductionEditorTabs.xml); restoring replaces the current editor layout and skips files that are no longer available. Snapshots from newer unsupported formats are left untouched rather than restored.

Enhance Structure View

Since 2026.08.3 · Maturity Stable

Master switch for all BetterPy structure-view additions. When enabled, BetterPy wraps PyCharm's Python structure view model and plugs in the filters and icons described below. Turning it off restores the stock PyCharm structure view exactly, and the individual sub-options become unavailable.

The wrapper is transparent: grouping, sorting, PyCharm's own filters, and the tree's expand/collapse behaviour are preserved.

How to invoke: Settings → Tools → BetterPy → IDE Customizations → Structure View.

Filter private members

Since 2026.08.3 · Maturity Stable

Adds a filter that hides private members — names starting with a single underscore (_helper) or a double underscore (__mangled) — so you can focus on a module's or class's public API.

Dunder members such as __init__ and __repr__ are part of the public protocol of a class and stay visible.

How to invoke: Click the filter icon in the Structure tool window → toggle "BetterPy: Show private members". The same toggle is available in the file structure popup (⌘F12 / Ctrl+F12).

class Repository:
    def __init__(self, session): ...
    def get(self, key): ...
    def _connect(self): ...
    def __build_query(self): ...
class Repository:
    def __init__(self, session): ...
    def get(self, key): ...

Hide overloads

Since 2026.05.0 · Maturity Stable
Structure View showing the Hide overloads filter before and after overload signatures are hidden
Structure View before and after hiding overload signatures

Hides typing.overload signatures from the structure view whenever the implementation is present, so a function that carries several overloads takes a single row instead of one row per signature.

Overload-only declarations — typically in .pyi stubs or protocols, where no implementation follows — stay visible, because hiding them would leave nothing behind. Aliased imports such as from typing import overload as _overload are recognised too.

How to invoke: Click the filter icon in the Structure tool window → toggle "BetterPy: Show overload signatures". Overloads are hidden by default; check the box to bring them back.

from typing import overload

@overload
def parse(value: int) -> int: ...

@overload
def parse(value: str) -> str: ...

def parse(value: int | str) -> int | str:  # only this row is shown
    return value

Icons for pytest tests and fixtures

Since 2026.08.3 · Maturity Stable · Issues PY-60483

Renders pytest tests, pytest fixtures, and plain helper functions with distinct icons in the structure view, so you can tell at a glance which functions are collected by pytest and which are just helpers.

In test files and in conftest.py, two extra filters let you independently show or hide test functions and pytest fixtures. Plain helper functions remain visible. Both pytest kinds are shown by default.

How to invoke: Icons appear automatically. The filters live behind the filter icon in the Structure tool window: "BetterPy: Show test functions" and "BetterPy: Show pytest fixtures".

import pytest

@pytest.fixture       # fixture icon
def client(): ...

def test_login(client):  # test icon
    ...

def make_payload():   # plain function icon
    ...

Filtering

Message console filter

Since 2026.04.14 · Maturity Stable · Issues PY-55937

BetterPy turns selected Python run-console and test-console output into clickable links, so console text can take you back to the related test, file, or class.

The feature is passive: run tests or Python code normally, then click the highlighted part of a supported console line. If multiple Python classes match the same class name, BetterPy opens a chooser.

Pytest node IDs

Pytest node IDs link to the matching test function, method, class, or parametrized test case.

tests/test_orders.py::test_creates_order
tests/test_orders.py::TestCheckout::test_rejects_empty_cart
tests/test_orders.py::test_creates_order[premium-user]

The full node ID is linked. Parametrized IDs may contain nested brackets, quotes, or class reprs:

Pytest short summaries

Pytest failure and error summaries link the file or node ID before the - separator.

FAILED tests/test_orders.py::TestCheckout::test_rejects_empty_cart - AssertionError: ...
ERROR tests/test_orders.py::test_creates_order - RuntimeError: ...
FAILED tests/test_orders.py - AttributeError: module 'src.orders' has no attribute 'Order'

When a summary includes only a file path, BetterPy links the file. When it includes a full node ID, BetterPy links the test target.

Python class reprs

Python class reprs link the class name inside <class ...> output.

<class 'src.adapters.outbound.http.HttpAdapter'>
<class "src.adapters.outbound.http.HttpAdapter">

Only the class portion is highlighted, for example HttpAdapter. Nested classes are highlighted from the first class-like segment:

<class 'src.domain.OuterClass.InnerClass'>

AttributeError object types

AttributeError lines that include an object type link the reported class name.

AttributeError: 'GetCephFileSystemsQuery' object has no attribute 'name'
E   AttributeError: 'CheckoutService' object has no attribute 'submit'

For these lines, BetterPy highlights the object type itself, such as CheckoutService.

Object reprs

Object reprs link the represented object's class.

self = <src.adapters.outbound.http.HttpAdapter object at 0x10bbf0530>
value = <src.domain.orders.OrderCreated object at 0x10b7a7d90>
client = <redis.asyncio.client.Redis(<redis.asyncio.connection.ConnectionPool(...)>)>

BetterPy highlights the class name, such as HttpAdapter or OrderCreated. For constructor-style reprs, each fully qualified class fragment is linked separately.

Exception class lines

Pytest traceback exception lines link class-like dotted names after the E prefix.

E           src.repositories.errors.RepositoryItemNotFound
E           src.domain.orders.OrderAlreadySubmitted

Names must look class-like, meaning at least one dotted segment starts with an uppercase letter. Plain variable-like names are ignored to avoid false positives.


Structural Search Profile (Python)

Since 2026.04.12 · Maturity Incubating · Issues PY-15003

Enables the IntelliJ Structural Search dialog for Python files and adds a Python profile for pattern matching and constraints. Use it to find and replace code patterns across your project.

This is an incubating integration. Making Structural Search genuinely useful for Python will likely require extensive testing on real projects and user feedback on templates, placeholders, constraints, and Python-specific syntax equivalences.

How to invoke: Edit → Find → Search Structurally…

Try it yourself: paste the snippet below into a Python file, then open Search Structurally… and enter the pattern. The $name$ tokens are SSR placeholders — each one matches any single expression or identifier.

x.append(1)
y.append(2)
z.extend([3])
$obj$.append($arg$)

This pattern matches the two .append(...) calls but not the .extend(...) call. Open Edit variables… in the Structural Search dialog to add constraints — for example, restrict $obj$ to a specific type, set a count range on a placeholder, or require a regex on its text.

Learn more: see the IntelliJ Platform docs for Structural search and replace and the Search templates, modifiers, and script constraints reference for placeholder and modifier syntax.