原文出處:http://blog.theerrorlog.com/how-coverage-py-produce-coverage-statistics.html
在Erlang下,用rebar可以在運行單元測試的同時調用cover模塊,從而得
到單元測試的覆蓋率,這是很方便的一個功能。最近發現Python下的nose也可
以通過--with-coverage
選項達到類似的效果,而這個選項實際上是調用了 coverage.py.
出於好奇,我研究了一下coverage.py的工作原理。
coverage.py的使用方法
抄一下coverage.py主頁的例子:
-
用
coverage
命令運行你的程序$ coverage run my_program.py arg1 arg2 blah blah ..your program's output.. blah blah
-
生成報告
1 2 3 4 5 6 7
$ coverage report -m Name Stmts Miss Cover Missing ------------------------------------------------------- my_program 20 4 80% 33-35, 39 my_other_module 56 6 89% 17-23 ------------------------------------------------------- TOTAL 76 10 87%
-
或者,生成更漂亮的HTML報告
$ coverage html
好吧對於一個開發工具來說,coverage
算是挺成功的,因爲夠傻瓜。和 nosetests
搭配使用的時候甚至更簡單,加一個參數就可以了,“猴子都能學
會”……
既然這裏有個腳本入口coverage
,我們就從這裏開始吧~
工作原理
首先從源碼庫把代碼clone下來,觀察一下setup.py
:
1 2 3 4 5 |
scripts = [
'coverage = coverage:main',
'coverage%d = coverage:main' % sys.version_info[:1],
'coverage-%d.%d = coverage:main' % sys.version_info[:2],
]
|
這裏說明,coverage
命令其實是調用了coverage
模塊下的main
函數,根據這個
函數的內容——爲了簡潔起見這裏省略掉一堆處理命令行參數的代碼——我們可以跟蹤到 coverage/control.py
文件中的Coverage
類。
這個類包含了所有從數據採集到生成報告的代碼,我們只關心它如何採集到程序執行 數據就好了。而“採集數據”這個操作,通過這裏的源碼可以推斷出,是由 coverage/collector.py
中的Collector
類和PyTracer
類,又或者是CTracer
類合作完成的。
PyTracer
和CTracer
其實在邏輯上是等價的,只不過一個是純Python實現,而另
一個是C語言實現。爲什麼會這樣呢?我們先繼續看下去……
既然這兩個類是一樣的,我們還是來看稍微漂亮一點的Python版本吧。PyTracer
的start
函數有句docstring說,這個函數是用來“Return
a Python function suitable for use with sys.settrace()”的。
好吧,真相大白了,coverage.py利用了Python虛擬機的trace機制。我怎麼就沒想 到呢?
sys.settrace(...)
這個函數的文檔在這裏,所以參數和用法說明什麼的我就省略了。實際上 這是Python程序調試機制的核心——幾乎每種虛擬機或者操作系統,都有類似的 機制,用於干預其上執行的程序,可以認爲這是給對應的調試器開的“後門”。 例如Erlang下有erlang:trace(...),POSIX系統下也有ptrace; Erlang的dbg模塊正是基於erlang:trace(...)的,而gdb也是在ptrace基 礎上工作的。
大家都知道調試器都是牛逼到可以把運行中的程序拆開再裝回去的,它們依賴 的trace機制自然也是大殺器。但是這貨通常要啓動各種鉤子(Hook),會嚴 重拖慢被trace的程序的運行速度,所以一般不會用在生產環境中。
這也就解釋了爲什麼coverage.py裏會有兩個tracer:tracer代碼基本上在 Python虛擬機的每步執行中都要被調用一遍,所以有一個C語言實現是能有效 提高效率的。那爲什麼還需要Python實現?這是爲了支持CPython以外的虛擬 機,像PyPy、Jython之類。
其他利用trace機制的有趣玩意
pycallgraph能生成Python程序中的函數調用關係圖(Call Graph)。
另外我在很~久之前用ptrace做了一個更改cd
命令行爲的小玩具 :)(轉載注:作者寫的)