ready-4 IT

Pylance am Limit: Memory Leaks bei High-Frequency File Operations bändigen

Diese Website & mein Tooling – heute: Pylance vs. ClutchPoint

Was passiert, wenn man eine VS Code Extension entwickelt, die tausende Dateioperationen pro Sekunde ausführt — und Pylance versucht, jede einzelne davon zu indizieren? Genau das ist mir bei der Entwicklung von ClutchPoint passiert.

Der Extension Host wurde schwerer, die UI begann zu ruckeln, und dank meiner eigenen Diagnose-Extension Gist Factor VS konnte ich den Übeltäter isolieren: ms-python.vscode-pylance.

  • Über 500 aktive, nicht bereinigte Listener während schneller Löschzyklen
  • Event-Loop-Blocking durch einen überlasteten Extension Host
  • Massiver RAM-Anstieg — der Garbage Collector kam nicht mehr hinterher

Ich schreibe das hier als Erfahrungsbericht — inklusive der genauen Konfiguration — weil Pylance in Standard-Projekten gold wert ist, aber in ungewöhnlichen Umgebungen zur echten Falle werden kann.

📅 Veröffentlicht: 21. März 2026  ·  Zuletzt aktualisiert: 21. März 2026  ·  © 2026 Nejat Philip Eryigit · ready-4-IT.com

Die Pylance-Diät — 3 Schritte gegen Observer-Leaks


TL;DR (Die „Pylance-Diät")

Wenn du nur eine Sache sofort ändern willst:

// settings.json
{
  "python.analysis.diagnosticMode": "openFilesOnly"
}

Das allein stoppt den aggressivsten RAM-Verbrauch. Der Rest der Konfiguration folgt weiter unten.


Die Problemfindung: Wenn der Language Server zum Flaschenhals wird

Bei der Entwicklung von ClutchPoint — einer VS Code Extension für automatisierte High-Frequency-File-Operationen — stellte ich fest, dass VS Code bei hoher Dateifrequenz massiv zu ruckeln begann.

Dank meiner Diagnose-Extension Gist Factor VS konnte ich den Übeltäter schnell isolieren: ms-python.vscode-pylance.

Das Problem: Pylance versucht, jede neu erstellte oder geänderte Datei sofort zu indizieren. Bei einem Workflow, der hunderte temporäre Dateien erzeugt, führt das zu:

  • Massiven Memory Leaks: Der RAM-Verbrauch schießt hoch, ohne wieder zu sinken.
  • Event-Loop Blocking: Pylance belegt den Extension Host so stark, dass andere Extensions — und die UI selbst — verzögert reagieren.
  • Leaking Listeners: Über 500 aktive, nicht bereinigte Listener während schneller Löschzyklen nachweisbar.

Die Analyse: Warum Pylance „leckt"

Pylance basiert auf Pyright. In Standard-Projekten ist das Verhalten — tiefes Indizieren aller Workspace-Dateien — gewollt und sinnvoll. In einer High-Frequency-Umgebung oder bei großen Workspaces (z.B. in einer Vagrant-Entwicklungsumgebung) führt dieser „Eifer" jedoch dazu, dass:

  1. Der Garbage Collector nicht mehr mit dem Aufräumen hinterherkommt.
  2. Jedes neue File-Event eine neue Analyse-Task triggert — und die Queue kontinuierlich wächst.
  3. Event-Listener für Dateiänderungen registriert, aber nicht sauber wieder abgemeldet werden.

Das Ergebnis ist ein klassischer Observer-Leak auf Extension-Host-Ebene.


Die Lösung: Die „Pylance-Diät"

Um die Stabilität wiederherzustellen, ohne auf Pylances Intelligenz zu verzichten, haben wir eine dreistufige Strategie implementiert:

1) Konfiguration optimieren (settings.json)

Der wichtigste Schritt ist, Pylance den „Eifer" zu nehmen:

{
  "python.analysis.diagnosticMode": "openFilesOnly",
  "python.analysis.indexing": false,
  "python.analysis.packageIndexDepths": [
    { "name": "", "depth": 1 }
  ],
  "python.analysis.typeCheckingMode": "basic",
  "python.analysis.persistAllIndices": false
}

Wichtig: Diese Einstellungen sind bewusst restriktiv. Für Teams mit großen Python-Monorepos können sie den Komfort etwas einschränken. Als Kompromiss empfiehlt sich der Einsatz in Workspace-Settings statt User-Settings — so gilt die Diät nur für belastete Projekte.

Spalten
"python.analysis.diagnosticMode"
"openFilesOnly"
Nur geöffnete Dateien werden analysiert. Der effektivste Hebel gegen RAM-Leaks.
"python.analysis.indexing"
false
Stoppt das großflächige Hintergrund-Indizieren des gesamten Workspaces.
"python.analysis.packageIndexDepths"
[{"name": "", "depth": 1}]
Begrenzt, wie tief Pylance in installierte Bibliotheken eintaucht.
"python.analysis.typeCheckingMode"
"basic"
Reduziert Rechenaufwand ohne auf grundlegendes Type-Checking zu verzichten.
"python.analysis.persistAllIndices"
false
Reduziert Festplatten-I/O durch weniger Cache-Dateien auf der Disk.

2) Architektonische Anpassung: Throttling in ClutchPoint

In der Extension-Logik von ClutchPoint haben wir ein Batching eingeführt. Anstatt Pylance mit jedem einzelnen File-Event zu bombardieren, werden Operationen gruppiert gepusht. Damit hat der Language Server Zeit zum „Atmen":

// Vereinfacht — Batching statt jeden einzelnen Save direkt triggern
const pendingFiles = new Set<string>();
let batchTimer: NodeJS.Timeout | undefined;

function scheduleAnalysis(uri: vscode.Uri) {
  pendingFiles.add(uri.fsPath);
  if (batchTimer) clearTimeout(batchTimer);
  batchTimer = setTimeout(() => {
    // Pylance nur einmal pro Batch informieren
    pendingFiles.clear();
    batchTimer = undefined;
  }, 500); // 500ms Debounce
}

3) Transparenz für den Nutzer: Gist Factor VS

Durch Gist Factor VS geben wir dem Nutzer jetzt Feedback: Wenn die Systemlatenz durch Pylance steigt, erscheint ein Hinweis im Editor. So wird klar, dass nicht die eigene App, sondern der Language Server gerade am Limit arbeitet — ein „neutraler Schiedsrichter" zwischen Extension und Editor.


Bonus: Was Gist Factor VS noch fand — LEAK_WEBVIEW_NO_DISPOSED_CHECK

Während der Analyse der Pylance-Leaks hat Gist Factor VS parallel einen zweiten, weit verbreiteten Fehlertyp in VS Code Extensions aufgedeckt: LEAK_WEBVIEW_NO_DISPOSED_CHECK.

Hier ein repräsentativer Auszug aus dem echten Diagnose-Output (gekürzt, Original: 35 Treffer in 8 Dateien):

[
  {
    "code": "LEAK_WEBVIEW_NO_DISPOSED_CHECK",
    "severity": 4,
    "message": "webview.postMessage called without a check for a disposed panel.",
    "source": "gistfactor",
    "startLineNumber": 209
  },
  {
    "code": "LEAK_WEBVIEW_NO_DISPOSED_CHECK",
    "severity": 4,
    "message": "webview.postMessage called without a check for a disposed panel.",
    "source": "gistfactor",
    "startLineNumber": 175
  }
]

Was bedeutet das?

Das Pattern ist simpel, aber gefährlich: Eine Extension sendet Nachrichten an ein Webview-Panel — ohne zu prüfen, ob das Panel noch offen ist.

  1. Nutzer schließt einen Dashboard-Tab → Panel wird disposed.
  2. Ein asynchroner Prozess läuft noch und will ein Ergebnis senden.
  3. webview.postMessage wird aufgerufen → Fehler oder Zombie-Referenz im Speicher.

In einem Hochfrequenz-Szenario (ClutchPoint triggert ständig File-Events, die Dashboards aktualisieren wollen) summiert sich das zu einem echten Leak-Problem.

Der Fix: SafeMessenger

Anstatt überall if (this.panel)-Abfragen einzustreuen, löst ein zentraler Wrapper das Problem ein für alle Mal:

// src/runtime/safeMessenger.ts
import * as vscode from 'vscode';

export class SafeMessenger {
    private _isDisposed = false;

    constructor(private readonly _panel: vscode.WebviewPanel) {
        this._panel.onDidDispose(() => {
            this._isDisposed = true;
        });
    }

    public postMessage(message: unknown): void {
        if (this._isDisposed) {
            return; // Panel bereits geschlossen — Nachricht wird verworfen
        }
        try {
            this._panel.webview.postMessage(message);
        } catch (err) {
            console.error('[SafeMessenger] Failed to send message:', err);
        }
    }

    public get isDisposed(): boolean {
        return this._isDisposed;
    }
}

Vorher (risikoreich):

this.panel.webview.postMessage({ command: 'update', data: payload }); // ❌

Nachher (Gist Factor konform):

this.messenger.postMessage({ command: 'update', data: payload }); // ✅

Dieser Wrapper ist auch der Grundstein für das Disturber Radar in Gist Factor VS: Fehlgeschlagene Sendeversuche können geloggt und im Dashboard sichtbar gemacht werden — so sieht man auf einen Blick, welche Extensions aktiv versuchen, bereits geschlossene Panels zu bespielen.


Alternativen: Wenn die Diät nicht reicht

Falls die Konfiguration allein nicht ausreicht, gibt es zwei sinnvolle Alternativen:

Ruff (als Pylance-Entlastung)

Ruff ist ein in Rust geschriebenes Linting- und Formatting-Tool — extrem schnell und ressourcenschonend. Es kann Pylance beim Linting komplett ersetzen und den Language Server auf Type-Checking reduzieren.

Voraussetzung: Die VS-Code-Extension charliermarsh.ruff muss installiert sein. Empfehlenswert ist dieser Eintrag in den VS-Code-User-Settings (%APPDATA%\Code\User\settings.json):

{
  "ruff.nativeServer": "on",
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff"
  }
}

Die reine Zuweisung editor.defaultFormatter = charliermarsh.ruff greift nicht, wenn die Extension fehlt.

Ruff ersetzt Pylance nicht — Aufgabenteilung auf einen Blick:

Funktion Pylance Ruff
IntelliSense / Autocomplete
Typ-Prüfung (via Pyright)
Go-to-Definition
Hover-Docs / Signaturen
Linting (flake8, pylint…) schwach
Formatting (Black-kompatibel)
Import-Sortierung (isort)
Geschwindigkeit normal 10–100× schneller

Pylance und Ruff — komplementäre Werkzeuge im Überblick

Kann Pylance durch Ruff ersetzt werden? Nur teilweise. Ruff übernimmt Linting und Formatting vollständig — aber kein IntelliSense, kein Type-Checking, keine Go-to-Definition. Wer diese IDE-Funktionen nutzt, braucht Pylance weiterhin.

Weiterführend: Pylance oder Ruff? Werkzeuge verstehen, richtig kombinieren

Für reine Script- und CLI-Projekte (Manifest-Prüfer, Automations-Tools, Skripte die primär im Terminal laufen statt in der IDE): Pylance kann projektspezifisch vollständig deaktiviert werden:

// .vscode/settings.json im Projektordner
{
  "python.languageServer": "None"
}

Ruff übernimmt dann Linting und Formatting — der Extension Host bleibt komplett entlastet, ohne andere Projekte zu beeinflussen.

BasedPyright (als Pylance-Ersatz)

BasedPyright ist ein Community-Fork des Pyright-Kerns — ressourceneffizienter als Pylance und ohne Microsoft-Telemetrie. Für Projekte, die dauerhaft unter Pylance leiden, eine ernstzunehmende Option.


Fazit

Pylance ist mächtig — aber nicht für jede Umgebung ausgelegt. Für Entwickler, die mit automatisierten Dateioperationen, großen Vagrant-Workspaces oder Monorepos arbeiten, ist eine „Diät" der Einstellungen oft unumgänglich, um den Extension Host stabil zu halten.

Der entscheidende erste Schritt:

"python.analysis.diagnosticMode": "openFilesOnly"

Beobachte danach den RAM-Verbrauch von pylance im Task Manager — der Unterschied ist in der Regel sofort sichtbar.

Kontext: Testumgebung - OS: Windows 10 Home / Windows 11 - VS Code: 1.80+ - Pylance: ms-python.vscode-pylance - Vagrant-Workspace mit mehreren offenen PHP/JS/Python-Projekten - Extension: ClutchPoint (High-Frequency File Operations) - Diagnose: Gist Factor VS

Support the Journey & Development! 🚀

If my IT guides or the Snapmaker Wiki saved your project (or your hardware), I'd appreciate a coffee! ☕
Your support doesn't just cover hosting and testing costs—it also fuels the development of my apps and tools. Every donation helps me dedicate more time to coding solutions that make our tech-life easier. Thank you for being part of this!

☕ Donate via PayPal