2011-01-19 103 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。