2012-11-22 24 views
4

我有一個程序依賴於打印大量不相關和煩人的消息的大型代碼庫。我想將它們清理一下,但由於它們的內容是動態生成的,我不能只爲它們而努力。如何找到打印語句的位置?

有沒有辦法在打印語句上放置鉤子? (我使用Python 2.4,但我會對任何版本的結果感興趣)。是否有另一種方法來查找輸出來自哪個「打印」語句?

+0

處理這個問題的通常方法是使用日誌庫 – Marcin

回答

3

對於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。

+0

如果我從__future__導入print_function',我不能再做任何'print 5'。這打破了現有的代碼: - /對此有何評論?我錯過了什麼嗎? – Alfe

+0

@Alfe:導入語句使'print'成爲一個函數。 (在Python2中'print'是一個聲明。)像所有函數一樣,你需要用圓括號括住參數。所以'print 5'必須(重新)寫成'print(5)'。請注意,在Python3中'print'是默認的函數,所以爲了將來的兼容性,總是使用圓括號是一個好習慣。 – unutbu

+0

您從「For CPython2.6 +」開始。我無法更改生產代碼中的每個打印語句(!)以使用函數。如果可以的話,我可以改變它來調用一個myPrint()函數,然後當然可以從它被調用的地方開始協議(引發異常,捕獲它,打印跟蹤或類似的東西)。問題是如何找到print *語句*。 – Alfe

2

非常總值的黑客攻擊,使這項工作:

使用自己喜歡的文本編輯器,使用搜索/查找功能。

找到所有打印語句。

並且手動向其中的每一個輸入數字或標識符。 (或者,如果自動這樣做有什麼腳本)

一個腳本來執行,這將是簡單的,只是在用它看爲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,而不是隻有一個計數器。

+0

他提到他試圖使用[grep](https://en.wikipedia.org/wiki/Grep),這種排除 – goncalopp

+1

它以什麼方式排除我的答案?如果打印總是打印動態輸出,他不能通過grep輸出來找到打印的位置。但是如果打印的是「id,dynamic info」,那麼他只能找到打印的id。 –

+1

我重讀過這個問題,你說的對,我的不好。我正在閱讀OP的「印刷品是動態生成的」(不僅是其內容)。這突然讓代碼庫看起來更「無聊」:) :) – goncalopp

1

這裏有一個技巧,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) 
+0

我不能給你它,但對於那個Java黑客+10000! –

+1

與Java不同,您不需要拋出一個虛假的異常來獲取當前的堆棧跟蹤,您可以使用'traceback.extract_stack()'代替。 –

+0

@LieRyan:謝謝,更新了我的答案。 –

1

在惡劣的環境下(輸出在一些奇怪的二進制庫中完成),您還可以使用strace -e write(以及更多選項)。如果你沒有閱讀strace的輸出結果,那麼這個特定的程序會一直等到你完成,所以你可以給它發一個信號,看看它死在哪裏。