Indata (.proto-schema)

Utdata (Python)

Vad det här verktyget gör

Du har ett Protocol Buffers-schema och en Python-tjänst eller ett skript som behöver matchande typer. Den officiella vägen är protoc med Python-pluginen (se den officiella Python-tutorialen) — den producerar genererade meddelandeklasser som fungerar men är jobbiga att läsa och bullriga i diffar. Det här verktyget spottar i stället ut vanliga dataclasses — rena, idiomatiska och lätta att mocka i tester. Klistra in schemat, kopiera utdata, släpp in i ditt projekt.

Typmappningen är vad du skulle skriva för hand. string/bytes blir str/bytes, bool blir bool, varje heltalsbredd (int32 upp till sfixed64) blir int, och double/float blir float. repeated T blir list[T] med field(default_factory=list), map<K, V> blir dict[K, V] med field(default_factory=dict), och singulära meddelandereferenser blir Optional[Msg] med default None, så cirkulära referenser och forward references bara fungerar.

Enum blir subklasser av IntEnum, vilket är vad den officiella protobuf-runtimen i Python använder internt och vad de flesta granskare förväntar sig att se. from __future__ import annotations ligger högst upp så att uppskjuten utvärdering hanterar forward refs rent — inga sträng-citerade type hints i kroppen behövs. Nästlade meddelanden plattas ut till dataclasses på toppnivå; Python tjänar inte på nästling som Java gör, och platta namn är lättare att importera. Allt körs i din webbläsare; inget av ditt schema lämnar sidan.

Så använder du det

Tre steg. Utdata är klar att släppa in i en <code>.py</code>-fil.

1

Klistra in ditt .proto-schema

Släpp schemat i den vänstra editorn. syntax = "proto3"; högst upp är okej men valfritt. Parsern hanterar nästlade message-block, enum-deklarationer, oneof, map<K, V> och fältoptions. Imports känns igen men hoppas över — klistra in importerade typer inline om ditt schema spänner över flera filer.

Fältnamn behålls som de är: order_id i .proto förblir order_id i Python. snake_case är redan Pythonic. Klassnamn behålls i PascalCase, i linje med PEP 8-konventioner.

2

Läs utdata

Den högra panelen visar Python med ett @dataclass per meddelande och en IntEnum-subklass per enum. Enum först, sedan meddelanden i beroendeordning (barn före föräldrar). Lägg till filen i ditt projekt, importera de dataclasses du behöver, klart.

3

Använd dataclasserna

Skapa instanser med nyckelordsargument, mutera dem som vanliga objekt, serialisera med dataclasses.asdict() eller json.dumps för HTTP-transport. Om du behöver fullständig Protobuf wire-format-encoding, koppla in dataclasserna i protobuf-python eller använd dem som typad shim framför din gRPC-klient.

När det faktiskt sparar tid

Skissa typer för en ny gRPC Python-tjänst

Du startar en ny tjänst som konsumerar ett befintligt Protobuf-API. Du vill ha rena dataclasses för request/response-formerna utan att redan köra protoc. Klistra in schemat, släpp utdata i types.py, skriv din affärslogik mot dataclasserna, koppla in gRPC Python senare när du är redo.

Mocka Protobuf-data i pytest

Genererade Protobuf-meddelandeklasser är jobbiga att bygga i tester eftersom varje fält har sin egen setter, och konstruktorerna inte tar alla fält som kwargs. Handgjorda dataclasses gör det — Order(order_id="ORD-42", customer_name="Ava Chen", total_amount=99.50) bara fungerar. Använd den här utdatan som fixtures och mockar och behåll de riktiga Protobuf-klasserna för wire-format-serialisering.

Granska en Protobuf-API-ändring

En backend-kollega lade till fält i Order och ett nytt OrderStatus-värde. Du vill veta vad din Python-klientkod måste hantera utan att dra hela bygget. Klistra in det nya .proto:t, gör en diff av dataclass-utdata mot dina nuvarande typer och lämna en fokuserad granskningskommentar.

Snabba skript och engångs-ETL-jobb

Du skriver ett 50-radigt skript för att backfilla data från en JSON-dump som följer ett Protobuf-schema. Att sätta upp protoc för ett engångsskript är overkill. Hämta dataclasserna härifrån, parsa JSON in i dem, kör skriptet, släng. Inget byggsteg, ingen toolchain, inga genererade filer som ligger kvar i repot.

Vanliga frågor

Skickas mitt schema någonstans?

Nej. Parsern och Python-emittern körs helt i din webbläsare som JavaScript. Öppna DevTools och titta på Network-fliken medan du klistrar in — noll requests. Praktiskt när schemat innehåller interna typnamn, paketsökvägar eller annat du helst inte vill skicka till en tredjepartstjänst.

Varför dataclasses i stället för de officiella protobuf-python-meddelandeklasserna?

Klasserna som protoc genererar fungerar, men är ordrika, svåra att mocka i tester och bullriga i code review. Dataclasses ger dig typade kwargs, jämförelse av likhet och ren repr på köpet. Behöver du wire-format-encoding kan du mappa mellan dataclasserna och de officiella meddelandetyperna i ett tunt adapterskikt — de flesta team tycker att den uppdelningen är bättre än att typa allt mot de genererade klasserna.

Varför IntEnum och inte enum med str-värden?

Protobuf-enum är heltalsvärden på wire-nivå — varje värde har ett tag-nummer. IntEnum matchar det exakt: OrderStatus.ORDER_STATUS_PAID är både en namngiven medlem och heltalet 2, vilket round-trippar rent via JSON eller wire format. Vill du ha StrEnum (Python 3.11+) för JSON-encodingen så gör du find-replace på IntEnum i utdata.

Varför är meddelandefält Optional[Msg] i stället för bara Msg?

I proto3 kan ett singulärt meddelandefält vara osatt (frånvaron är meningsfull, till skillnad från skalärer där default är nollvärdet). Att default är None matchar den semantiken och håller cirkulära referenser kompilerbara — om Order bäddar in en Address och adressen bäddar in tillbaka, så behöver inga av dataclasserna vara definierade först. from __future__ import annotations högst upp i filen gör att forward refs löses i runtime via PEP 563.

Hur hanteras map<K, V>?

Renderas som dict[K, V] med field(default_factory=dict) som default. Protobuf-mappar med icke-string-nycklar (map<int32, string>) blir dict[int, str]. JSON har bara stringnycklar, så när du serialiserar dicten till JSON blir int-nycklarna strängar — det är en egenhet hos proto3 JSON-specen, inte hos konverteraren.

Hanteras oneof?

Varje oneof-fält emitteras som ett vanligt dataclass-fält. Utdata tvingar inte regeln "exakt en" — för det vill du ha en Union-typ eller en diskriminerad struktur, vilket beror på hur din runtime modellerar exklusivitet. Den platta layouten är lätt att läsa och matchar vad de flesta Python-kodbaser gör i praktiken. Redigera för hand om du vill ha striktare typning.

Relaterade verktyg

Om du jobbar med Protobuf, JSON och Python passar dessa bra ihop: