我有一個程序依賴於打印大量不相關和煩人的消息的大型代碼庫。我想將它們清理一下,但由於它們的內容是動態生成的,我不能只爲它們而努力。如何找到打印語句的位置?
有沒有辦法在打印語句上放置鉤子? (我使用Python 2.4,但我會對任何版本的結果感興趣)。是否有另一種方法來查找輸出來自哪個「打印」語句?
我有一個程序依賴於打印大量不相關和煩人的消息的大型代碼庫。我想將它們清理一下,但由於它們的內容是動態生成的,我不能只爲它們而努力。如何找到打印語句的位置?
有沒有辦法在打印語句上放置鉤子? (我使用Python 2.4,但我會對任何版本的結果感興趣)。是否有另一種方法來查找輸出來自哪個「打印」語句?
對於CPython2。5歲以上:
import sys
import inspect
import collections
_stdout = sys.stdout
Record = collections.namedtuple(
'Record',
'frame filename line_number function_name lines index')
class MyStream(object):
def __init__(self, target):
self.target = target
def write(self, text):
if text.strip():
record = Record(*inspect.getouterframes(inspect.currentframe())[1])
self.target.write(
'{f} {n}: '.format(f = record.filename, n = record.line_number))
self.target.write(text)
sys.stdout = MyStream(sys.stdout)
def foo():
print('Hi')
foo()
產生
/home/unutbu/pybin/test.py 20: Hi
對於CPython2.6 +,我們可以用
from __future__ import print_function
導入打印功能,然後重定向它如我們所願:
from __future__ import print_function
import sys
import inspect
import collections
Record = collections.namedtuple(
'Record',
'frame filename line_number function_name lines index')
def myprint(text):
if text.strip():
record = Record(*inspect.getouterframes(inspect.currentframe())[1])
sys.stdout.write('{f} {n}: '.format(f = record.filename, n = record.line_number))
sys.stdout.write(text + '\n')
def foo():
print('Hi')
print = myprint
foo()
請注意inspect.currentframe
使用sys._getframe
這不是所有Python實現的一部分。所以上面的解決方案可能只適用於CPython。
如果我從__future__導入print_function',我不能再做任何'print 5'。這打破了現有的代碼: - /對此有何評論?我錯過了什麼嗎? – Alfe
@Alfe:導入語句使'print'成爲一個函數。 (在Python2中'print'是一個聲明。)像所有函數一樣,你需要用圓括號括住參數。所以'print 5'必須(重新)寫成'print(5)'。請注意,在Python3中'print'是默認的函數,所以爲了將來的兼容性,總是使用圓括號是一個好習慣。 – unutbu
您從「For CPython2.6 +」開始。我無法更改生產代碼中的每個打印語句(!)以使用函數。如果可以的話,我可以改變它來調用一個myPrint()函數,然後當然可以從它被調用的地方開始協議(引發異常,捕獲它,打印跟蹤或類似的東西)。問題是如何找到print *語句*。 – Alfe
非常總值的黑客攻擊,使這項工作:
使用自己喜歡的文本編輯器,使用搜索/查找功能。
找到所有打印語句。
並且手動向其中的每一個輸入數字或標識符。 (或者,如果自動這樣做有什麼腳本)
一個腳本來執行,這將是簡單的,只是在用它看爲print
用正則表達式,並與print ID,
替換它,然後它都將是相同的,但你將獲得數字。
歡呼聲。
編輯
禁止任何奇怪的格式,下面的代碼應該爲你做。
請注意,這只是您可以做到這一點的一個例子。不是一個真正的答案。
import re
class inc():
def __init__(self):
self.x = 0
def get(self):
self.x += 1
return self.x
def replacer(filename_in, filename_out):
i = inc()
out = open(filename_out, 'w')
with open(filename_in) as f:
for line in f:
out.write("%s\n" % re.sub(r'print', 'print %d,' % i.get(), line))
我使用了一個基本的增量類,以防您想要某種更復雜的ID,而不是隻有一個計數器。
嚴格來說,您所依賴的代碼庫不應該包含任何print
陳述。所以,你應該真的只是刪除所有其中。
除此之外,你可以猴子補丁stdout
:Adding a datetime stamp to Python print
這裏有一個技巧,Jeeeyul came up with for Java:有東西時,換行已經寫了告示更換輸出流(即sys.out
)。
如果此標誌爲真,則在寫入下一個字節時引發異常。在同一位置捕捉異常,直到找到不屬於「調試流編寫器」的代碼爲止。
僞代碼:
class DebugPrintln:
def __init__(self):
self.wasLF = False
def write(self, x):
if self.wasLF:
self.wasLF = False
frames = traceback.extract_stack()
... find calling code and output it ...
if x == '\n':
self.wasLF = true
super.write(x)
我不能給你它,但對於那個Java黑客+10000! –
與Java不同,您不需要拋出一個虛假的異常來獲取當前的堆棧跟蹤,您可以使用'traceback.extract_stack()'代替。 –
@LieRyan:謝謝,更新了我的答案。 –
在惡劣的環境下(輸出在一些奇怪的二進制庫中完成),您還可以使用strace -e write
(以及更多選項)。如果你沒有閱讀strace的輸出結果,那麼這個特定的程序會一直等到你完成,所以你可以給它發一個信號,看看它死在哪裏。
處理這個問題的通常方法是使用日誌庫 – Marcin