Skip to content

Distinguish protected fields from bound methods in variablePresentation #2023

@franklucky001

Description

@franklucky001

Problem

When using variablePresentation to reduce noise in the debugger variables view, protected: "hide" currently hides both protected/private fields and protected/private bound methods.

For example:

class Service:
    def __init__(self):
        self._config = {"timeout": 10}
        self._client = object()
        self._state = "ready"

    def _build_url(self):
        return "https://example.com"

    def _validate(self):
        return True


service = Service()
breakpoint()

With a launch configuration like:

{
  "variablePresentation": {
    "special": "hide",
    "function": "hide",
    "class": "group",
    "protected": "hide"
  }
}

the debugger hides _config, _client, and _state, even though these are object state fields that are usually useful during debugging.

If protected is changed to inline or group, the fields become visible again, but protected bound methods such as _build_url and _validate are also shown, adding noise to the variables view.

Expected behavior

It would be useful if variablePresentation could distinguish protected/private data fields from protected/private bound methods.

For example, something like:

{
  "variablePresentation": {
    "special": "hide",
    "function": "hide",
    "class": "group",
    "protected": "group",
    "protectedFunction": "hide",
    "protectedField": "inline"
  }
}

or another equivalent naming scheme.

The goal is to support this behavior:

  • Hide __dunder__ special variables.
  • Hide function/method variables.
  • Hide or group class-related variables.
  • Keep protected/private instance fields visible.
  • Hide protected/private bound methods.

Actual behavior

Currently, protected appears to be based primarily on the variable name pattern, such as _xxx, rather than distinguishing whether the value is a field or a bound method.

As a result:

self._config      # useful field, should remain visible
self._client      # useful field, should remain visible
self._build_url   # bound method, often noise
self._validate    # bound method, often noise

are all affected by the same protected presentation option.

Why this matters

In Python applications, many important object state fields are intentionally named with a leading underscore:

self._config
self._client
self._session
self._state
self._registry

Hiding all protected variables makes object instances difficult to inspect because the useful state disappears.

On the other hand, keeping protected variables visible often exposes many bound methods, which makes the variables panel noisy.

This is especially noticeable in editors that rely directly on debugpy / DAP variable presentation, such as VS Code or Zed, where the debugger variables panel can become much noisier than IDEs that implement additional Python-specific variable filtering.

Suggested solution

Add more granular variablePresentation options for protected variables, such as:

{
  "variablePresentation": {
    "protectedField": "inline",
    "protectedFunction": "hide"
  }
}

or allow function: "hide" to reliably apply to bound methods even when they are also classified as protected.

A possible classification rule could be:

  • If the name starts with _ and the value is callable / method-like, treat it as a protected function/method.
  • If the name starts with _ and the value is not callable, treat it as a protected field.

Alternative workaround

Currently the closest workaround is:

{
  "variablePresentation": {
    "special": "hide",
    "function": "hide",
    "class": "group",
    "protected": "group"
  }
}

However, this still groups useful fields together with protected methods and does not provide a clean object-state view.

Another workaround is to use watch expressions such as:

vars(obj)
obj.__dict__

but this does not solve the variables panel noise directly.

Environment

  • debugpy version:
  • Python version: 3.12
  • Editor / client: Zed / VS Code / other
  • OS: macos

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions