Eingabe (.proto-Schema)

Ausgabe (Python)

Was dieses Tool macht

Du hast ein Protocol Buffers-Schema und einen Python-Service oder ein Skript, das passende Typen braucht. Der offizielle Weg ist protoc mit dem Python-Plugin (siehe das offizielle Python-Tutorial) — die generierten Message-Klassen funktionieren, sind aber unangenehm zu lesen und laut in Diffs. Dieses Tool gibt stattdessen einfache Dataclasses aus — sauber, idiomatisch und in Tests leicht zu mocken. Schema einfügen, Output kopieren, ins Projekt fallen lassen.

Das Type-Mapping ist genau das, was du von Hand schreiben würdest. string/bytes werden zu str/bytes, bool zu bool, jede Integer-Breite (int32 bis sfixed64) zu int, und double/float zu float. repeated T wird zu list[T] mit field(default_factory=list), map<K, V> zu dict[K, V] mit field(default_factory=dict), und einzelne Message-Referenzen werden zu Optional[Msg] mit Default None, sodass zirkuläre und Forward-Referenzen einfach laufen.

Enums werden zu Subklassen von IntEnum — das nutzt das offizielle protobuf-Python-Runtime intern und das erwarten die meisten Reviewer zu sehen. from __future__ import annotations steht oben, damit die verzögerte Auswertung Forward-Refs sauber abhandelt — keine string-quoted Type Hints im Body nötig. Verschachtelte Messages werden zu Top-Level-Dataclasses geflattened; Python profitiert von Verschachtelung nicht so wie Java, und flache Namen lassen sich einfacher importieren. Alles läuft im Browser; nichts vom Schema verlässt die Seite.

So nutzt du es

Drei Schritte. Die Ausgabe lässt sich direkt in eine <code>.py</code>-Datei droppen.

1

Füge dein .proto-Schema ein

Lass das Schema in den linken Editor fallen. syntax = "proto3"; oben ist okay, aber optional. Der Parser kann verschachtelte message-Blöcke, enum-Deklarationen, oneof, map<K, V> und Feld-Optionen. Imports werden erkannt, aber übersprungen — füge importierte Typen inline ein, falls dein Schema mehrere Dateien umfasst.

Feldnamen bleiben wie sie sind: order_id in .proto bleibt order_id in Python. snake_case ist sowieso schon pythonisch. Klassennamen bleiben in PascalCase, passend zu PEP-8-Konventionen.

2

Lies die Ausgabe

Das rechte Panel zeigt Python mit einem @dataclass pro Message und einer IntEnum-Subklasse pro Enum. Erst die Enums, dann die Messages in Abhängigkeitsreihenfolge (Kinder vor Eltern). Datei zum Projekt hinzufügen, die nötigen Dataclasses importieren, fertig.

3

Nutze die Dataclasses

Erzeuge Instanzen mit Keyword-Argumenten, mutiere sie wie normale Objekte, serialisiere mit dataclasses.asdict() oder json.dumps für HTTP-Transport. Wenn du volles Protobuf-Wire-Format-Encoding brauchst, bring die Dataclasses in protobuf-python ein oder nutze sie als getypten Shim vor deinem gRPC-Client.

Wann es wirklich Zeit spart

Typen für einen neuen gRPC-Python-Service skizzieren

Du startest einen neuen Service, der eine bestehende Protobuf-API konsumiert. Du willst saubere Dataclasses für die Request/Response-Formen, ohne schon protoc zu starten. Schema einfügen, Output in types.py droppen, Business-Logik gegen die Dataclasses schreiben, später gRPC Python dranhängen, wenn du soweit bist.

Protobuf-Daten in pytest mocken

Generierte Protobuf-Message-Klassen sind in Tests mühsam zu konstruieren, weil jedes Feld seinen eigenen Setter hat und die Konstruktoren nicht alle Felder als kwargs nehmen. Hand-gerollte Dataclasses tun das — Order(order_id="ORD-42", customer_name="Ava Chen", total_amount=99.50) funktioniert einfach. Nimm diese Ausgabe als Fixtures und Mocks und behalte die echten Protobuf-Klassen für Wire-Format-Serialisierung.

Eine Protobuf-API-Änderung reviewen

Ein Backend-Kollege hat Order Felder hinzugefügt und einen neuen OrderStatus-Wert. Du willst wissen, was dein Python-Client-Code abfangen muss, ohne den vollen Build zu ziehen. Neues .proto einfügen, Dataclass-Output gegen deine aktuellen Typen diffen, fokussierten Review-Kommentar hinterlassen.

Schnelle Skripte und einmalige ETL-Jobs

Du schreibst ein 50-Zeilen-Skript, um Daten aus einem JSON-Dump nachzuladen, das einem Protobuf-Schema folgt. protoc für ein Einmal-Skript aufzusetzen ist Overkill. Hol dir die Dataclasses hier, parse das JSON in sie, lass das Skript laufen, wirf es weg. Kein Build-Schritt, keine Toolchain, keine generierten Dateien, die im Repo herumliegen.

Häufige Fragen

Wird mein Schema irgendwohin geschickt?

Nein. Parser und Python-Emitter laufen vollständig im Browser als JavaScript. Öffne die DevTools und beobachte den Network-Tab beim Einfügen — null Requests. Praktisch, wenn dein Schema interne Typnamen, Package-Pfade oder andere Dinge enthält, die du lieber nicht zu einem Drittanbieter schickst.

Warum Dataclasses statt der offiziellen protobuf-python-Message-Klassen?

Die von protoc generierten Klassen funktionieren, sind aber wortreich, schwer in Tests zu mocken und laut im Code-Review. Dataclasses geben dir getypte kwargs, Gleichheitsvergleich und sauberen repr gratis. Brauchst du Wire-Format-Encoding, kannst du in einem dünnen Adapter-Layer zwischen den Dataclasses und den offiziellen Message-Typen mappen — die meisten Teams finden diese Trennung besser, als alles gegen die generierten Klassen zu typisieren.

Warum IntEnum und keine Enums mit str-Wert?

Protobuf-Enums sind auf Wire-Ebene int-wertig — jeder Wert hat eine Tag-Nummer. IntEnum deckt das exakt ab: OrderStatus.ORDER_STATUS_PAID ist gleichzeitig benanntes Member und die Ganzzahl 2 und macht einen sauberen Round-Trip durch JSON oder Wire-Format. Wenn du ein StrEnum (Python 3.11+) für die JSON-Codierung willst, mach im Output ein Find-Replace auf IntEnum.

Warum sind Message-Felder Optional[Msg] statt einfach Msg?

In proto3 kann ein einzelnes Message-Feld unset sein (das Fehlen ist bedeutungstragend, anders als bei Skalaren, wo der Default der Nullwert ist). Ein None-Default passt zu dieser Semantik und hält zirkuläre Referenzen kompilierbar — wenn Order ein Address einbettet und das Address zurück, muss keine der Dataclasses zuerst definiert sein. from __future__ import annotations oben in der Datei sorgt via PEP 563 dafür, dass Forward-Refs zur Laufzeit aufgelöst werden.

Wie wird map<K, V> behandelt?

Wird als dict[K, V] mit field(default_factory=dict) als Default ausgegeben. Protobuf-Maps mit Nicht-String-Schlüsseln (map<int32, string>) werden zu dict[int, str]. JSON kennt nur String-Schlüssel, also werden int-Schlüssel beim Serialisieren zu Strings — das ist eine Eigenheit der proto3-JSON-Spec, nicht des Konverters.

Wird oneof unterstützt?

Jedes oneof-Feld wird als reguläres Dataclass-Feld ausgegeben. Die Ausgabe erzwingt die "genau eins"-Bedingung nicht — dafür bräuchtest du einen Union-Typ oder eine diskriminierte Struktur, was davon abhängt, wie dein Runtime Exklusivität modelliert. Das flache Layout liest sich gut und entspricht dem, was die meisten Python-Codebases in der Praxis tun. Wenn du strengere Typisierung brauchst, editiere von Hand.

Verwandte Tools

Wenn du mit Protobuf, JSON und Python arbeitest, passen diese gut zusammen: