2017-06-01 66 views
1

我導入了readline,爲我的程序添加了自定義的選項卡完成。現在我需要保存程序的輸出,但是任何重定向stdout的嘗試都會破壞選項卡完成功能。Python模塊「readline」無法處理輸出重定向

我試過python3 script.py | tee txt.txt女巫似乎最接近打印到標準輸出和文本文件,但它不起作用。

這裏是自定義選項卡完成類與函數調用主(以防萬一):

import readline 

class MyCompleter(object): 

    def __init__(self, options): 
     self.options = sorted(options) 

    def complete(self, text, state): 
     if state == 0: 
      if text: 
       self.matches = [s for s in self.options if s and s.startswith(text)] 
      else: 
       self.matches = self.options[:] 
     try: 
      return self.matches[state] 
     except IndexError: 
      return None 

def readlineset(a): # function called from main to turn on tab completion 
        # a is list of strings 
    readline.set_completer(MyCompleter(a).complete) 
    readline.parse_and_bind('tab: complete') 
+2

[可能相關的Python問題](https://bugs.python.org/issue24829)。背景:readline被設計用於在交互式終端上工作,但將輸出重定向到其他地方(即使它將它回顯給你,就像'tee'),使終端對readline看起來是非互動的。 –

+1

如果你想記錄交互式會話的輸出(而不是'tee'),你可能更喜歡使用'typecript'? –

回答

0

這裏是一個可能的解決方案:「猴子補丁」的sys.stdout.write方法,以便任何寫入stdout也得到發送到一個文件。我承認這不完全優雅,但它的工作原理。 ;)

我製作了Logger class a Context Manager,以便它可以在with語句中使用。

import readline 
import sys 

class MyCompleter(object): 
    def __init__(self, options): 
     self.options = sorted(options) 

    def complete(self, text, state): 
     if state == 0: 
      if text: 
       self.matches = [s for s in self.options if s and s.startswith(text)] 
      else: 
       self.matches = self.options[:] 
     try: 
      return self.matches[state] 
     except IndexError: 
      return None 

class Logger(object): 
    ''' Monkey-patch sys.stdout 
     to copy output to a file 
    ''' 
    def __init__(self, fname): 
     self.fh = open(fname, 'w') 
     self.oldwrite = sys.stdout.write 
     sys.stdout.write = self.write 

    def write(self, s): 
     self.oldwrite(s) 
     self.fh.write(s) 

    def close(self): 
     self.fh.close() 
     sys.stdout.write = self.oldwrite 

    # Define Context Manager methods so Logger 
    # can be used in a `with` statement 
    def __enter__(self): 
     return self 

    def __exit__(self, *args): 
     self.close() 
     return False 

def readlineset(a): 
    ''' Turn on tab completion. 
     `a` is list of strings that will be completed 
    ''' 
    readline.set_completer(MyCompleter(a).complete) 
    readline.parse_and_bind('tab: complete') 

def main(): 
    readlineset(['python', 'stack', 'overflow', 'exchange']) 
    with Logger('mylog.txt'): 
     while True: 
      s = input('> ') 
      if s == 'quit': 
       break 
      print(repr(s), len(s)) 

    print('bye') 

if __name__ == '__main__': 
    main() 

演示

> This is a test 
'This is a test' 14 
> python on stack overflow 
'python on stack overflow' 24 
> quit 
bye 

mylog.txt

'This is a test' 14 
'python on stack overflow' 24 

如果你不想使用with,您可以使用Logger這樣的:

def main(): 
    readlineset(['python', 'stack', 'overflow', 'exchange']) 
    logger = Logger('mylog.txt') 
    while True: 
     s = input('> ') 
     if s == 'quit': 
      break 
     print(repr(s), len(s)) 

    logger.close() 
    print('bye')