2013-02-06 66 views
3

這可能是有史以來最愚蠢的問題,但我想知道是否有辦法在函數(最好是裝飾器)上編寫一個包裝器,以便您可以捕獲在該函數中引發異常的情況下,局部變量的內部狀態。它會在創建它們時捕獲本地人,醃製它們,然後在沒有例外的情況下將它們處置,或者如果發現任何異常,則將它們寫入文件。對於長時間運行的python腳本的失效保護

這是太奇怪了,還是有人像這樣的東西愚弄?

回答

5

您可以捕獲f_locals variable on a frame in the traceback

import sys 
import functools 

def capturelocals(func): 
    @functools.wraps(func) 
    def wrapperfunc(*args, **kw): 
     try: 
      return func(*args, **kw) 
     except Exception: 
      _, _, tb = sys.exc_info() 
      try: 
       while tb.tb_next is not None: 
        tb = tb.tb_next # find innermost frame 
       locals = tb.tb_frame.f_locals 
       print locals 
      finally: 
       del tb # prevent leaking tracebacks 
      raise 
    return wrapperfunc 

爲了證明它的工作原理:

>>> @capturelocals 
... def foobar(): 
...  foo = 'bar' 
...  spam = 'eggs' 
...  raise ValueError('Bam!') 
... 
>>> foobar() 
{'foo': 'bar', 'spam': 'eggs'} 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in wrapperfunc 
    File "<stdin>", line 5, in foobar 
ValueError: Bam! 
+0

我想這應該讓我開始,謝謝馬亭。 – BenDundee

+0

如果'del tb'沒有執行,會發生什麼情況?它不妨礙代碼通過刪除相關的回溯來進一步檢查異常嗎?我必須承認,我對幀,回溯等知之甚少,但我很好奇,所以任何信息都會受到歡迎。 :) – EOL

+0

'del tb'只會清除* local *變量。它可能指向'tb'在*中定義的幀,一個參考循環,所以我們明確地清除它。 –

1

另一種選擇是將作爲參數傳遞給函數只是那些你有興趣節省變量在發生災難性事件的情況下。

裝飾器可以幫助你做到這一點:

import os 
try: 
    import cPickle as pickle 
except ImportError: 
    import pickle 

def save_args_if_exception(pickle_folder): 
    def real_decorator(function): 
     def wrapper(*args, **kwargs): 
      try: 
       function(*args, **kwargs) 
      except: 
       print 'FAILING SAFE: Saving arguments to folder:' 
       print pickle_folder 
       for arg_position, arg in enumerate(args, start=1): 
        arg_fname = os.path.join(pickle_folder, str(arg_position) + 
              '.pickle') 
        print 'Saving positional argument-{}'.format(arg_position) 
        with open(arg_fname, mode='wb') as f: 
         pickle.dump(arg, f) 
       print 'Saving keyword arguments (kwargs)' 
       with open(os.path.join(pickle_folder, 'kwargs.pickle'), 
          mode='wb') as f: 
        pickle.dump(kwargs, f) 
       raise # re-raise original exception 
     return wrapper 
    return real_decorator 

@save_args_if_exception(os.getcwd()) 
def important_function(first_arg, second_arg=2, **kwargs): 
    print 'About to raise an exception!' 
    raise Exception 

important_function(3) 

注意,在這個例子中,second_arg不保存到一個文件,大概是因爲Python的處理默認參數的方式。根據你的情況,這可能或可能不是有用的行爲。

參考文獻:

+0

這實際上是我正在考慮的方向。它可能足以抓住args和kwargs ... – BenDundee

+0

請注意,在玩這個遊戲之後,它會保存'first_arg'和任何關鍵字參數('** kwargs'),但是'second_arg'不會保存。在某些情況下,這可能是有用的行爲。 –