2011-01-19 2 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, чтобы избежать большей части работы. Это не задокументировано, поэтому используйте источник, Люк.

+0

Фактически, open() работает одинаково, используется как менеджер контекста или как класс. Так что это может иметь смысл, и это возможно. –

0

Попробуйте перемещение @contextmanager в нижней части списка декораторов.

Смежные вопросы