Girdi (.proto şeması)

Çıktı (Python)

Bu araç ne yapar

Elinizde bir Protocol Buffers şeması ve karşılığı tiplere ihtiyaç duyan bir Python servisi ya da scripti var. Resmi yol, Python plugin'iyle protoc'tur (resmi Python eğitimine bakın); çalışan ama okunması zahmetli ve diff'lerde gürültücü mesaj sınıfları üretir. Bu araç onun yerine düz dataclass'lar üretir — temiz, deyimsel ve testlerde mock'laması kolay. Şemayı yapıştırın, çıktıyı kopyalayın, projenize bırakın.

Tip eşlemesi, elle yazsanız ne yazardıysanız o. string/bytes str/bytes'a, bool bool'a, her tamsayı genişliği (int32'den sfixed64'e kadar) int'e ve double/float float'a dönüşür. repeated T field(default_factory=list)'li list[T]'ye, map<K, V> field(default_factory=dict)'li dict[K, V]'ye, tekil mesaj referansları ise varsayılanı None olan Optional[Msg]'a dönüşür; böylece dairesel referanslar ve forward reference'lar sorunsuz çalışır.

Enum'lar IntEnum alt sınıflarına dönüşür; resmi protobuf-python runtime'ının dahili olarak kullandığı ve çoğu kod inceleyicinin görmeyi beklediği şey budur. from __future__ import annotations dosyanın en üstünde durur; böylece ertelenmiş değerlendirme forward ref'leri temiz biçimde halleder — gövdede tırnak içi type hint gerekmez. İç içe mesajlar üst seviye dataclass'lara düzleştirilir; Python iç içe yapılardan Java gibi fayda sağlamaz ve düz isimleri import etmek daha kolaydır. Her şey tarayıcınızda çalışır; şemanızdan hiçbir şey sayfadan dışarı çıkmaz.

Nasıl kullanılır

Üç adım. Çıktı, doğrudan bir <code>.py</code> dosyasına bırakılmaya hazır.

1

.proto şemanızı yapıştırın

Şemayı sol taraftaki editöre bırakın. En üstteki syntax = "proto3"; iyi olur ama isteğe bağlıdır. Parser; iç içe message blokları, enum tanımları, oneof, map<K, V> ve alan seçeneklerini işler. Import'lar tanınır ama atlanır — şemanız birden fazla dosyaya yayılıyorsa içe aktarılan tipleri satır içi yapıştırın.

Alan adları olduğu gibi kalır: .proto'daki order_id, Python'da da order_id olarak kalır. snake_case zaten Pythonic. Sınıf adları da PascalCase'de kalır ve PEP 8 kurallarıyla uyumludur.

2

Çıktıyı okuyun

Sağ panelde her mesaj için bir @dataclass, her enum için bir IntEnum alt sınıfıyla Python kodu yer alır. Önce enum'lar, sonra bağımlılık sırasında mesajlar (çocuklar ebeveynlerden önce) gelir. Dosyayı projenize ekleyin, ihtiyaç duyduğunuz dataclass'ları import edin, hepsi bu.

3

Dataclass'ları kullanın

Anahtar kelimeli argümanlarla örnek oluşturun, normal nesneler gibi değiştirin, HTTP taşıma için dataclasses.asdict() ya da json.dumps ile serileştirin. Tam Protobuf wire-format kodlamasına ihtiyacınız varsa, dataclass'ları protobuf-python'a bağlayın ya da gRPC istemcinizin önünde tipli bir shim olarak kullanın.

Gerçekten zaman kazandıran durumlar

Yeni bir gRPC Python servisi için tipleri taslaklamak

Mevcut bir Protobuf API'sini tüketen yeni bir servise başlıyorsunuz. Henüz protoc çalıştırmadan, request/response şekilleri için temiz dataclass'lar istiyorsunuz. Şemayı yapıştırın, çıktıyı types.py'e bırakın, iş mantığınızı dataclass'lara karşı yazın, hazır olduğunuzda gRPC Python'u sonra bağlayın.

pytest'te Protobuf verisini mock'lamak

Üretilen Protobuf mesaj sınıflarını testlerde inşa etmek sıkıcıdır; her alanın kendi setter'ı vardır ve constructor'lar tüm alanları kwargs olarak kabul etmez. Elle yazılmış dataclass'lar kabul eder — Order(order_id="ORD-42", customer_name="Ava Chen", total_amount=99.50) sorunsuz çalışır. Bu çıktıyı fixture ve mock olarak kullanın, wire-format serileştirme için gerçek Protobuf sınıflarını saklayın.

Bir Protobuf API değişikliğini incelemek

Backend'deki bir takım arkadaşı Order'a yeni alanlar ve yeni bir OrderStatus değeri ekledi. Tam build'i çalıştırmadan, Python istemci kodunuzun neyi ele alması gerektiğini görmek istiyorsunuz. Yeni .proto'yu yapıştırın, dataclass çıktısını mevcut tiplerinizle diff'leyin, odaklı bir review yorumu bırakın.

Hızlı script'ler ve tek seferlik ETL işleri

Protobuf şemasını izleyen bir JSON dump'tan veriyi geri doldurmak için 50 satırlık bir script yazıyorsunuz. Tek seferlik bir script için protoc kurmak abartı. Buradan dataclass'ları alın, JSON'u onlara parse edin, script'i çalıştırın ve atın. Build adımı, toolchain ya da repoda artakalan üretilmiş dosya yok.

Sık sorulan sorular

Şemam herhangi bir yere gönderiliyor mu?

Hayır. Parser ve Python emitter, tarayıcınızda tamamen JavaScript olarak çalışır. DevTools'u açın ve yapıştırırken Network sekmesini izleyin — sıfır istek. Şemanızda dahili tip adları, paket yolları ya da üçüncü taraf bir servise göndermek istemediğiniz bir şey varsa kullanışlıdır.

Resmi protobuf-python mesaj sınıfları yerine neden dataclass?

protoc'tan üretilen sınıflar çalışır ama uzun, testlerde mock'lanması zor ve code review'da gürültücüdür. Dataclass'lar size tipli kwargs, eşitlik karşılaştırması ve temiz repr'ı bedava verir. Wire-format kodlamaya ihtiyacınız varsa, dataclass'lar ile resmi mesaj tipleri arasında ince bir adapter katmanında eşleyebilirsiniz — çoğu ekip her şeyi üretilen sınıflara karşı tipiklemekten daha iyi bir bölüştürme bulur.

Neden str değerli enum'lar değil de IntEnum?

Protobuf enum'ları wire seviyesinde int değerlidir — her değerin bir tag numarası vardır. IntEnum buna tam uyar: OrderStatus.ORDER_STATUS_PAID hem isimli bir üye hem de 2 tamsayısıdır ve JSON ya da wire format üzerinden temiz şekilde gidip gelir. JSON kodlaması için StrEnum (Python 3.11+) istiyorsanız, çıktıdaki IntEnum'u find-replace ile değiştirin.

Mesaj alanları neden yalnızca Msg değil de Optional[Msg]?

proto3'te tekil bir mesaj alanı atanmamış olabilir (varsayılanı sıfır olan skalar tiplerin aksine, yokluk anlamlıdır). Varsayılanı None tutmak bu semantiği karşılar ve dairesel referansların derlenmesini sağlar — Order bir Address içeriyor ve address de geri içeriyorsa, dataclass'lardan hiçbirinin diğerinden önce tanımlanması gerekmez. Dosyanın en üstündeki from __future__ import annotations, PEP 563 üzerinden forward ref'lerin çalışma zamanında çözümlenmesini sağlar.

map<K, V>'yi nasıl ele alır?

Varsayılanı field(default_factory=dict) olan dict[K, V] olarak render edilir. String olmayan anahtarlı (map<int32, string>) Protobuf map'leri dict[int, str]'ye dönüşür. JSON yalnızca string anahtar destekler; bu yüzden dict'i JSON'a serileştirdiğinizde int anahtarlar string olur — bu, dönüştürücünün değil, proto3 JSON spesifikasyonunun bir özelliğidir.

oneof'u ele alıyor mu?

Her oneof alanı, sıradan bir dataclass alanı olarak yayımlanır. Çıktı "tam olarak bir tane" kısıtını dayatmaz — bunun için bir Union tipi ya da diskriminantlı bir yapı isterdiniz; bu, runtime'ınızın dışlamayı nasıl modellediğine bağlıdır. Düz yerleşim okunması kolaydır ve çoğu Python kod tabanının pratikte yaptığıyla örtüşür. Daha sıkı tipleme istiyorsanız elle düzenleyin.

İlgili araçlar

Protobuf, JSON ve Python ile çalışıyorsanız bunlar iyi gider: