[Python-ideas] @contextlib.contextmanager(with_context=True) - passing a context to contextmanager
Nick Coghlan
ncoghlan at gmail.com
Fri Jul 10 09:09:03 CEST 2015
On 10 July 2015 at 07:34, Giampaolo Rodola' <g.rodola at gmail.com> wrote:
>
> @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
There isn't anything in this syntax which says to me "designed to be
used as a class method decorator", nor in the invocation that says
"this CM gets access to the class or instance object" :)
The mechanism underlying the context-manager-or-decorator behaviour is
actually https://docs.python.org/3/library/contextlib.html#contextlib.ContextDecorator,
and that's entirely unaware of both the function being decorated *and*
its runtime arguments. This means it is unaware of the details of
method invocations as well.
If you want a method aware context manager, you'll likely want to
write a decorator factory that accepts the relevant CM as a parameter.
For example (untested):
def with_cm(cm):
def decorator(f):
@functools.wraps(f)
def wrapper(self, *args, **kwargs):
with cm(self):
return f(self, *args, **kwargs)
Used as:
class Process:
@with_cm(wrap_exceptions)
def exe():
...
Regardless, I'd advise against trying to hide the fact that there's an
extra step going on in order to make the function being wrapped and/or
one or more of its arguments available to the context manager, as that
doesn't happen by default.
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
More information about the Python-ideas
mailing list