decorator needs access to variables where it is used.

Chris Angelico rosuav at gmail.com
Wed Oct 9 07:02:32 EDT 2019


On Wed, Oct 9, 2019 at 9:53 PM Antoon Pardon <antoon.pardon at vub.be> wrote:
>
> I have some logging utilities so that when I write library code, I just use the following.
>
> from logutil import Logger
>
> log = Logger(__name__)

Are you always absolutely consistent with this? Do you always have it
as a module-level variable and always called "log", and you never use
the name "log" for anything else? (Namespaced usage like "math.log" is
fine, but you never have a function-local "log" or anything.)

> And from then on I just use log, to do the logging of that module.
>
> But now I would like to write a decorator trace, so that a decorated
> function would log, its arguments and result.
>
> Something like:
>
> def trace(func):
>
>     def wrapper(*args):
>         log(DEBUG, "=> %s%s" % (func.__name__, args))
>         result = func(*args)
>         log(DEBUG, "%s => %s" (func.__name__, result))
>
>     return wrapper
>
> The problem is that with this decorater, the log function
> used, will be the log function defined where the decorator
> is written and I would prefer it to use the log function
> where it is used, so that it uses the same log function
> as the function it decorates.
>

The decorator has full access to the function object, including a
reference to that function's module globals.

def trace(func):
    log = func.__globals__["log"]
    ... proceed as before

As long as you can depend on "log" always being a module-level
(global) name, and not (for instance) a closure variable, this should
work. It's a bit ugly, but it should be fine since it's buried away in
the decorator.

ChrisA



More information about the Python-list mailing list