开发者

How can I mix decorators with the @contextmanager decorator?

开发者 https://www.devze.com 2023-02-05 15:55 出处:网络
Here is the code I\'m working with: from contextlib import contextmanager from functools import wraps class with_report_status(object):

Here is the code I'm working with:

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)
  开发者_JS百科      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

The problem is, @with_report_status does not yield, as expected by @contextmanager. However, I can't wrap it the other way around either, because @contextmanager returns a generator object (i think!) instead of the value itself.

How can I make @contextmanager play nice with decorators?


Try moving @contextmanager at the bottom of the decorator list.


That is kind of a weird question: @contextmanager returns a context manager, not a generator. But for some reason you want to treat that context manager like a function? That's not something you can make work, they have nothing in common.

I think what you want is a MyClass.make_workspace that is context manager and also has a report_status field in case of exceptions. For that you need to write a context manager yourself that sets this field in it's __exit__ method, @contextmanager can't help you here.

You can subclass contextlib.GeneratorContextManager to avoid most of the work. It's not documented, so use the source, Luke.

0

精彩评论

暂无评论...
验证码 换一张
取 消