2011-01-19 105 views
3

這是我的工作代碼:如何將裝飾器與@contextmanager裝飾器混合使用?

from contextlib import contextmanager 
from functools import wraps 
class with_report_status(object): 

    def __init__(self, message): 
     self.message = message 

    def __call__(self, f): 
     @wraps(f) 
     def wrapper(_self, *a, **kw): 
      try: 
       return f(_self, *a, **kw) 
      except: 
       log.exception("Handling exception in reporting operation") 
       if not (hasattr(_self, 'report_status') and _self.report_status): 
        _self.report_status = self.message 
       raise 

     return wrapper 

class MyClass(object): 

    @contextmanager 
    @with_report_status('unable to create export workspace') 
    def make_workspace(self): 
     temp_dir = tempfile.mkdtemp() 
     log.debug("Creating working directory in %s", temp_dir) 
     self.workspace = temp_dir 
     yield self.workspace 
     log.debug("Cleaning up working directory in %s", temp_dir) 
     shutil.rmtree(temp_dir) 

    @with_report_status('working on step 1') 
    def step_one(self): 
     # do something that isn't a context manager 

的問題是,@with_report_status不屈服,通過@contextmanager預期。但是,我無法以其他方式換行,因爲@contextmanager會返回一個生成器對象(我認爲!)而不是該值本身。

我怎樣才能讓@contextmanager與裝飾者一起玩呢?

回答

1

這是一個奇怪的問題:@contextmanager返回一個上下文管理器,而不是生成器。但出於某種原因,您希望將該上下文管理器視爲一個函數?這不是你能做的工作,他們沒有共同之處。

我想你想要的是一個MyClass.make_workspace是上下文管理器,並且在有例外的情況下也有一個report_status字段。爲此,您需要自己編寫一個上下文管理器,它將此字段設置爲__exit__方法,@contextmanager不能幫助您。

您可以繼承contextlib.GeneratorContextManager以避免大部分工作。它沒有記錄,所以請使用源代碼Luke。

+0

實際上,open()以相同的方式工作,用作上下文管理器或類。所以它是有道理的,而且這是可能的。 – 2012-07-23 09:39:53

0

嘗試移動裝飾列表底部的@contextmanager。