ply解析protobuf
github: https://github.com/LiuRoy/proto_parser
安裝使用
下載代碼進入目錄之后執行:
pyton setup.py install
當前支持直接讀取proto文件構造客戶端,使用方法參考example/client.py
import grpc
from protoparser import make_client
client = make_client('./helloworld.proto')
channel = grpc.insecure_channel('localhost:50051')
stub = client.GreeterStub(channel)
response = stub.SayHello(client.HelloRequest(name='abc'))
print("Greeter client received: " + response.name)
代碼講解
本人接觸到的rpc通信協議有thrift和protobuf,兩者很類似。雖然原生的thrift和protobuf編譯器針對不同語言都只能將接口文件轉換為可以使用的中間文件,但不同於其他靜態語言,python的動態類型可以很方便的將接口文件直接加載使用,例如thrift的python第三方庫thriftpy,使用起來比原生庫要方便很多。但個人覺得thriftpy自己造輪子的成分過多,client、servier以及底層的通信實現全部重寫了一套,不是很確定能否和原生框架無縫對接。參考thriftpy,自己也實現了一個簡單的protobuf編譯器,解析生成的結果也盡量用原生對象。
左邊是官方提供的protobuf使用流程,具體參考鏈接http://www.grpc.io/docs/quickstart/python.html,可以看出每次修改完protobuf文件都需要用protobuf compiler重新生成中間文件,使用上不是很友好,期望的目標是右邊那樣,只需要重啟一下進程就可以了。
詞法分析
詳見文件lexer.py,直接使用第三方庫ply做的詞法解析。詞法分析算法上很復雜,但是使用起來卻很直觀,就是將輸入文本按照自定義的規則解析為一個一個的符號。詳細內容可以參考lex文檔。
語法分析
詳見文件grammar.py,也是直接使用的第三方庫ply做的語法分析,具體的文法參考的thriftpy,thriftpy的語法解析用到了很多全局變量,雖然實現簡單而且好理解,最大的缺陷就是不是線程安全,不能支持多線程,所以具體的實現上做了一些調整。調整內容有一下:
- 語法上為proto3的子集,protoparser解析沒有問題的接口文件原生編譯器一定能解析,反之不一定
- enum packed singular import reserved option oneof等關鍵詞不支持,因為用不上,所以也就懶得實現
- 不支持在message仲嵌套定義message,但是可以使用已經定義的message類型
- 因為是在最后做的符號檢查,支持用到的符號在后面定義
沒有編譯基礎的童鞋閱讀ply的yacc文檔可能比較迷茫,建議看一下編譯原理仲關於語法分析相關的概念和算法,在此推薦胡倫均的編譯原理,每一個概念都會給很多例子解釋,很適合入門學習。至於輪子哥推薦的parsing techniques以及龍虎鯨書,可以作為進階學習使用。
ply的語法分析使用了LRLR算法,這是一種至下而上的分析法,也就是從給定的輸入串開始,根據文法規則逐步進行歸約,直至歸約到文法開始的符號,或者說從語法樹的末端開始,步步向上歸約,直至根節點的分析方法。自定義的函數也是在對應的文法歸約的時候執行,理解了這一點對使用ply很重要,定義好文法之后,按照文法一步一步實現歸約函數,寫語法解析也是一件很有意思的事情。
類型轉換
詳見objects.py和parser.py,自己實現的語法解析直接解析為原生的對象,而是按照自己的文法邏輯先解析成objects.py仲定義的類型,然后在parser.py仲做了一些轉換,由於沒有相關的文檔,實在是不清楚原生對象的使用方法,所以盲人摸象般的寫了最簡單類型的轉關規則。本來以為python動態類型用起來很美好,但是真的大量使用的時候,不僅不好調試,而且連自己也看不懂寫了啥玩意兒。歡迎有興趣的同伴提修改建議。