2016-04-11 5 views
3

私は、ユーザーの入力からprotobufsをパックしなければならないビルドシステムを持っています。ユーザーはフォームのフィールドを入力して提出し、ビルド・システムはプロトタイプをパックし、途中で送信します。ビルドシステムは、処理しているタイプを認識していなければなりません。それ以外の場合は、プロジェクトチームが.protosを変更するたびに、それらをサポートするためにビルド変更を行う必要があります。protobuf拡張プロパティのタイプを取得するにはどうすればよいですか?

これの大部分は機能します。 .protosは次のようになります。

#dataobj_base.proto 
message DataObj { 
    extensions 100 to 199; 
    optional string property1 = 1; 
} 

#dataobj.proto 
message DataObjExtension { 
    optional string ext_property1 = 1; 
} 

extend DataObj { 
    optional DataObjExtension generic_extension = 100; 
} 

....これはサポートするのに十分簡単です。今まで私のビルドシステムは、それが書いている型の名前を知ることによってgeneric_extensionオブジェクトにアクセスしました。私はDataObj_pb2とDataObjExtension_pb2をインポートして、このようにそれらを使用することができます。素晴らしい作品

def do_the_thing(type_name, property1, ext_property1): 
    #type_name, in this case, is 'DataObj' 
    game_module = (
    importlib.import_module('generated_protobufs.{}_pb2'.format(type_name))) 
    ext_module = (
    importlib.import_module('generated_protobufs.{}Extension_pb2'.format(type_name))) 
    instance_constructor = getattr(game_module, type_name) 
    instance = instance_constructor() 

    #instance is now an instance of DataObj 
    instance.property1 = property1 
    setattr(pb_instance.Extensions[ext_module.generic_extension], 
      property_name, property_value) 
} 

をが、プロジェクトチームは、同じベースから複数のタイプを派生します。 DataObj "base"で動作する2つの異なるタイプが必要な場合は、これが破損します。私はこれを行うことができるようにしたい:

extend DataObj { 
    optional SomeOtherBaseObj generic_extension = 100; 
} 

トラブルは、私のPythonコードで、私はgeneric_extensionの種類を知っておく必要があり、ということです。

extensions = instance.Extensions[SomeOtherBaseObj_pb2.generic_extension] 

を...とextensions.__class__は、私が知る必要があるすべてを言うだろう:私は私が処理していたタイプの知識を持つコードを書いていた場合、私はどうなります。

エクステンション内のプロパティの名前を取得する方法はありますか(特にそのタイプ情報を含む)? .protosを大幅に改善するためには何百万もの方法がありますが、既にそれに依存しているかなりのコードベースがあります。フィールドを追加すると細かい部分が売れるかもしれませんが、大きな構造変更はできません。

拡張機能でプロパティのリストを取得できたとしても、私は金色になります。私はこのようにそれをハックしたい:

extend DataObj { 
    optional SomeOtherBaseObj generic_extension = 100; 
    optional bool generic_extension_type__SomeOtherBaseObj = 101; 
} 

generic_extension_typeは__...くれgeneric_extensionのためのタイプの名前を与えることよりも、理由もなくそこに他のだろう。拡張子を含むメッセージのインスタンスに設定

extend DataObj { 
    optional SomeOtherBaseObj generic_extension__SomeOtherBaseObj = 100; 
} 

答えて

2

instance.ListFields()リストのすべてのフィールド、:私はそれを行うことができればしかし、私はそれとを簡素化することができます。リスト内の各項目は、フルタイプの情報とフィールド値を含むFieldDescriptorのタプルです。 (FieldDescriptorはまた、あなたがExtensionsマップへのキーとして使用したいものである。)を行うことができますので:

for field, value in instance.ListFields(): 
    assert value == instance.Extensions[field] 
    print field.full_name, value 

を使用すると、メッセージにはまだ入っていない拡張子を見つけたい場合は、あなたが持っていますいくつかのオプション:

  1. DataObj._extensions_by_name対応する拡張のためのFieldDescriptor Sに(my_pkg.generic_extensionのような)完全修飾いるProtobufのシンボル名辞書マッピングです。

  2. DataObj._extensions_by_numberは、対応する内線番号のフィールド番号(あなたの場合は100〜199)をFieldDescriptorにマッピングします。

既知のすべての拡張機能について知るには、どちらのdictでも繰り返し実行できます。

残念ながら、これらは両方とも技術的にプライベートであり、将来のリリースで破損する可能性があります。現在、AFAIKの公開インタフェースはありません。しかし、これらのメンバは最初のリリースから存在しており、純粋なPythonとC-エクステンションの両方の実装に存在しています。さらに、 "extensions"機能全体がproto3で削除されていますので、このインタフェースが気にならなくなってしまうのは心配しているようです。

また、これらの「グローバルレジストリ」に頼るのはあまり設計されていないと考えられます。より良い設計は、あなたのコンポーネントが知る必要のあるすべての拡張のリストを渡すようにコンポーネントを初期化するコードのためのものです。いくつかの高水準コードは、プログラムの他のコンポーネントをポーリングして、必要な拡張機能を要求し、網羅的なリストを作成し、それを渡す必要があります。このアプローチにより、実際にどこの拡張機能が使用されているのか、デッドコード "を削除することができます。しかし、私は、既存のコードベースが常によく書かれているわけではないことを認識しており、「正しい方法」を実装することは、それが価値あるものよりも難しいことになります。

+0

ListFields()には、すでに値を持つプロパティがリストされています。私は空のインスタンスを構築してそれを移入する人なので、これはうまくいきません。 – Sniggerfardimungus

+1

@Sniggerfardimungus:これは質問からは分かりませんでしたが、今私は答えをさらに情報で広げました。 –

+0

私はプロジェクトの最初の調査の一環として_extensions_by_n *を使っていましたが、両方ともオブジェクトに拡張値を追加するまで空の辞書を返します。 = [ – Sniggerfardimungus

関連する問題