[Python-ideas] @contextlib.contextmanager(with_context=True) - passing a context to contextmanager
Giampaolo Rodola'
g.rodola at gmail.com
Thu Jul 9 23:34:19 CEST 2015
contextlib.contextmanager in Python 3 added the ability to define functions
which work both as decorators and context managers, which is great and one
of the features I appreciate the most about Python 3 (seriously).
I am facing a use case where I find contextlib.contextmanager has some
limitations though. I have a decorator which looks like this:
def wrap_exceptions(fun):
"""Decorator which translates bare OSError and IOError exceptions
into NoSuchProcess and AccessDenied.
"""
@functools.wraps(fun)
def wrapper(self, *args, **kwargs):
# here "self" is the Process class instance
try:
return fun(self, *args, **kwargs)
except EnvironmentError as err:
if err.errno in (errno.ENOENT, errno.ESRCH):
raise NoSuchProcess(self.pid, self._name)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
return wrapper
...and I use it like this:
class Process:
@wrap_exceptions
def exe():
...
The two key things about this decorator are:
- it's designed to be used with class methods
- it has a reference to the method's class instance (self)
I would like to push this a bit further and make wrap_exceptions() work
also a contextmanager, which is what contextlib.contextmanager should allow
me to do in an easy way. As contextlib.contextmanager stands right it won't
allow my use case though as there's no way to pass a reference of the class
instance (Process) to wrap_exceptions. So here is my proposal: what if we
add a new "with_context" argument to contextlib.contextmanager?
The resulting code would look like this:
@contextlib.contextmanager(with_context=True)
def wrap_exceptions(ctx):
# ctx is the Process class instance
try:
yield
except EnvironmentError as err:
pid = ctx.pid
name = ctx.name
if err.errno in (errno.ENOENT, errno.ESRCH):
raise NoSuchProcess(pid, name)
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(pid, name)
raise
class Process:
@wrap_exceptions()
def exe():
...
class Process:
def exe(self):
with wrap_exceptions(self):
...
It must be noted that:
- when with_context=True and wrap_exceptions is used as a decorator it can
only be used to decorate class methods and not regular functions
- when used as a decorator "self" is automatically passed as the first
argument for wrap_exceptions
I'm not sure if this is actually possible as I haven't gone through
contextlib.contextmanager in details (it's quite magical).
Thoughts?
--
Giampaolo - http://grodola.blogspot.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150709/dbebed30/attachment-0001.html>
More information about the Python-ideas
mailing list