2011-01-19 6 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가 예상대로 @with_report_status를 생성하지 않는다는 것입니다. 그러나 @contextmanager 생성기 개체를 반환하기 때문에 나는 둘 중 하나를 주위에 다른 방법으로 래핑 할 수 없습니다 (나는 생각합니다!) 값 자체 대신.

@contextmanager을 데코레이터로 멋지게 플레이하려면 어떻게해야합니까?

답변

1

이상한 질문입니다. @contextmanager은 생성기가 아닌 컨텍스트 관리자를 반환합니다. 그러나 어떤 이유로 당신은 컨텍스트 관리자를 함수처럼 취급하고 싶습니까? 그건 당신이 일할 수있는 것이 아니라 공통점이 없습니다.

내가 원하는 것은 MyClass.make_workspace이며 컨텍스트 관리자이고 예외의 경우에는 report_status 필드가 있다고 생각합니다. 이를 위해서는 __exit__ 메소드 인 @contextmanager에이 필드를 설정하는 컨텍스트 관리자를 직접 작성해야합니다.

대부분의 작업을 피하기 위해 contextlib.GeneratorContextManager의 하위 클래스로 지정할 수 있습니다. 문서화되어 있지 않으므로 출처를 사용하십시오, 루크.

+0

사실 open()은 컨텍스트 관리자 또는 클래스로 사용되는 것과 같은 방식으로 작동합니다. 그래서 그것은 의미가 있으며, 가능합니다. –

0

데코레이터 목록의 아래쪽에서 @contextmanager를 움직여 봅니다.

관련 문제