Protobuf-naar-Python-converter
Plak een .proto-schema. Krijg Python dataclasses met type hints, IntEnum-klassen voor enums en correcte defaults voor repeated- en map-velden.
Invoer (.proto-schema)
Uitvoer (Python)
Wat deze tool doet
Je hebt een Protocol Buffers-schema en een Python-service of script dat bijbehorende types nodig heeft. De officiële weg is protoc met de Python-plugin (zie de officiële Python-tutorial) — die produceert message-klassen die werken, maar lastig te lezen zijn en luidruchtig in diffs. Deze tool spuugt in plaats daarvan gewone dataclasses uit — schoon, idiomatisch en makkelijk te mocken in tests. Plak het schema, kopieer de uitvoer, drop het in je project.
De type-mapping is wat je met de hand zou schrijven. string/bytes worden str/bytes, bool wordt bool, elke int-breedte (int32 tot sfixed64) wordt int, en double/float worden float. repeated T wordt list[T] met field(default_factory=list), map<K, V> wordt dict[K, V] met field(default_factory=dict), en losstaande message-referenties worden Optional[Msg] met default None, zodat circulaire en forward references gewoon werken.
Enums worden subclasses van IntEnum, dat is wat de officiële Python protobuf-runtime intern gebruikt en wat de meeste reviewers verwachten te zien. from __future__ import annotations staat bovenaan zodat uitgestelde evaluatie de forward refs netjes afhandelt — geen type hints in stringaanhalingen in de body nodig. Geneste messages worden afgevlakt naar top-level dataclasses; Python heeft niet hetzelfde voordeel van nesting als Java, en platte namen zijn makkelijker te importeren. Alles draait in je browser; niets van je schema verlaat de pagina.
Hoe je het gebruikt
Drie stappen. De uitvoer is klaar om in een <code>.py</code>-bestand te droppen.
Plak je .proto-schema
Drop het schema in de linker editor. syntax = "proto3"; bovenaan is prima maar optioneel. De parser kan overweg met geneste message-blokken, enum-declaraties, oneof, map<K, V> en field options. Imports worden herkend maar overgeslagen — plak geïmporteerde types inline als je schema meerdere bestanden beslaat.
Veldnamen blijven zoals ze zijn: order_id in .proto blijft order_id in Python. snake_case is al Pythonic. Klassennamen blijven ook in PascalCase, conform PEP 8-conventies.
Lees de uitvoer
Het rechterpaneel toont Python met één @dataclass per message en één IntEnum-subclass per enum. Eerst de enums, dan de messages in afhankelijkheidsvolgorde (kinderen voor ouders). Voeg het bestand toe aan je project, importeer de dataclasses die je nodig hebt en klaar.
Gebruik de dataclasses
Maak instances aan met keyword arguments, muteer ze als gewone objecten, serialiseer met dataclasses.asdict() of json.dumps voor HTTP-transport. Heb je volledige Protobuf wire-format encoding nodig, sluit de dataclasses dan aan op protobuf-python of gebruik ze als getypte shim voor je gRPC-client.
Wanneer het echt tijd bespaart
Types schetsen voor een nieuwe gRPC Python-service
Je start een nieuwe service die een bestaande Protobuf-API consumeert. Je wilt schone dataclasses voor de request/response-vormen zonder al protoc te draaien. Plak het schema, drop de uitvoer in types.py, schrijf je business logic tegen de dataclasses, en hang er later gRPC Python aan als je zover bent.
Protobuf-data mocken in pytest
Gegenereerde Protobuf message-klassen zijn vervelend te bouwen in tests omdat elk veld zijn eigen setter heeft en de constructors niet alle velden als kwargs accepteren. Hand-gerolde dataclasses wel — Order(order_id="ORD-42", customer_name="Ava Chen", total_amount=99.50) werkt gewoon. Gebruik deze uitvoer als fixtures en mocks en houd de echte Protobuf-klassen voor wire-format-serialisatie.
Een Protobuf-API-wijziging reviewen
Een backend-collega heeft velden aan Order toegevoegd en een nieuwe OrderStatus-waarde. Je wilt weten wat je Python-client-code moet afhandelen zonder de hele build te draaien. Plak de nieuwe .proto, diff de dataclass-uitvoer tegen je huidige types en laat een gerichte review-comment achter.
Snelle scripts en eenmalige ETL-jobs
Je schrijft een script van 50 regels om data terug te vullen vanuit een JSON-dump die een Protobuf-schema volgt. protoc opzetten voor een eenmalig script is overkill. Pak hier de dataclasses, parse het JSON erin, draai het script, gooi het weg. Geen build-stap, geen toolchain, geen overgebleven gegenereerde bestanden in de repo.
Veelgestelde vragen
Wordt mijn schema ergens naartoe gestuurd?
Nee. De parser en de Python-emitter draaien volledig in je browser als JavaScript. Open de DevTools en hou het Network-tabblad in de gaten terwijl je plakt — nul requests. Handig als je schema interne typenamen, package-paden of iets anders bevat dat je liever niet naar een derde partij stuurt.
Waarom dataclasses in plaats van de officiële protobuf-python message-klassen?
De door protoc gegenereerde klassen werken, maar zijn omslachtig, lastig te mocken in tests en luidruchtig in code review. Dataclasses geven je getypte kwargs, gelijkheidsvergelijking en een schone repr gratis. Heb je wire-format-encoding nodig, dan kun je in een dunne adapter-laag mappen tussen de dataclasses en de officiële message-types — de meeste teams vinden dat een betere splitsing dan alles tegen de gegenereerde klassen typeren.
Waarom IntEnum en geen enums met str-waarde?
Protobuf-enums zijn op wire-niveau int-waarden — elke waarde heeft een tagnummer. IntEnum past daar precies op: OrderStatus.ORDER_STATUS_PAID is zowel een named member als het getal 2, en die round-tript schoon door JSON of wire format. Wil je een StrEnum (Python 3.11+) voor de JSON-encoding, doe dan een find-replace van IntEnum in de uitvoer.
Waarom zijn message-velden Optional[Msg] en niet gewoon Msg?
In proto3 kan een enkelvoudig message-veld unset zijn (de afwezigheid heeft betekenis, anders dan bij scalars waar de default de zero value is). Default None sluit aan bij die semantiek en houdt circulaire referenties compileerbaar — als Order een Address embed en het address embed terug, hoeft geen van beide dataclasses eerst gedefinieerd te zijn. from __future__ import annotations bovenaan het bestand zorgt via PEP 563 dat forward refs at runtime worden opgelost.
Hoe gaat het om met map<K, V>?
Wordt gerenderd als dict[K, V] met field(default_factory=dict) als default. Protobuf-maps met niet-string-sleutels (map<int32, string>) worden dict[int, str]. JSON kent alleen string-sleutels, dus als je het dict naar JSON serialiseert worden de int-sleutels strings — dat is een eigenaardigheid van de proto3 JSON-spec, niet van de converter.
Wordt oneof ondersteund?
Elk oneof-veld wordt uitgevoerd als een gewoon dataclass-veld. De uitvoer dwingt de "precies één"-constraint niet af — daarvoor wil je een Union-type of een gediscrimineerde structuur, wat afhangt van hoe je runtime exclusiviteit modelleert. De platte layout leest prettig en sluit aan bij wat de meeste Python-codebases in de praktijk doen. Pas met de hand aan als je strenger wilt typeren.
Gerelateerde tools
Als je met Protobuf, JSON en Python werkt, passen deze er goed bij: