Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

decorators only when __debug__ == True

2 views
Skip to first unread message

LX

unread,
Mar 29, 2010, 8:54:26 PM3/29/10
to
Hi all, I have a question about decorators. I would like to use them
for argument checking, and pre/post conditions. However, I don't want
the additional overhead when I run in non-debug mode. I could do
something like this, using a simple trace example.


@decorator
def pass_decorator(f, *args, **kw): # what about the slow-down that
incurs when using pass_decorator?
return f(*args, **kw)

@decorator
def trace_decorator(f, *args, **kw):
print "calling %s with args %s, %s" % (f.func_name, args, kw)
return f(*args, **kw)

trace_enable_flag = False #__debug__
trace = trace_decorator if trace_enable_flag else pass_decorator


Trouble is, there is still an additional encapsulating function call.
Is there any way to eliminate the extra function call altogether?
Thanks in advance!

MRAB

unread,
Mar 29, 2010, 9:34:27 PM3/29/10
to pytho...@python.org

I think you have misunderstood certain details about decorators.

This code with a decorator:

@decorator
def hello():
print "hello world"

basically does this:

def hello():
print "hello world"
hello = decorator(hello)

so your non-decorator just needs to return the function it was passed:

def pass_decorator(func):
return func

and your trace decorator would be something like this:

def trace_decorator(func):
def show(*args, **kwargs):
print "calling %s with args %s, %s" % (func.func_name,
args, kwargs)
result = func(*args, **kwargs)
print "returning %s from %s" % (result, func.func_name)
return result
return show

Steven D'Aprano

unread,
Mar 29, 2010, 10:11:09 PM3/29/10
to
On Mon, 29 Mar 2010 17:54:26 -0700, LX wrote:

> Hi all, I have a question about decorators. I would like to use them for
> argument checking, and pre/post conditions. However, I don't want the
> additional overhead when I run in non-debug mode. I could do something
> like this, using a simple trace example.


def decorator(func):
if __debug__:
@functools.wraps(func)
def inner(*args, **kwargs):
preprocess()
result = func(*args, **kwargs)
postprocess()
return result
return inner
else:
return func

--
Steven

LX

unread,
Mar 30, 2010, 4:43:42 PM3/30/10
to

Sure, but during runtime, pass_decorator will still be called, even
though it doesn't do anything. I am wondering if I can disable this
additional function call at non-debug runtime. In other words, I want
the calling stack to contain no decorator at all, not even
pass_decorator.

I should mention that, in my code, the symbol "decorator" is imported
from Michele Simionato's decorator.py file.

LX

unread,
Mar 30, 2010, 4:44:40 PM3/30/10
to
On Mar 29, 7:11 pm, Steven D'Aprano

Actually, I am using "decorator" from Michele Simionato's decorator.py
file, at http://pypi.python.org/pypi/decorator.

MRAB

unread,
Mar 30, 2010, 5:41:31 PM3/30/10
to pytho...@python.org

pass_decorator will be called when the decorated function is _defined_,
but not when the decorated function is _called_.

LX

unread,
Mar 31, 2010, 4:59:01 PM3/31/10
to
> >> so your non-decoratorjust needs to return the function it was passed:
>
> >>      def pass_decorator(func):
> >>          return func
>
> >> and your tracedecoratorwould be something like this:

>
> >>      def trace_decorator(func):
> >>          def show(*args, **kwargs):
> >>              print "calling %s with args %s, %s" % (func.func_name,
> >> args, kwargs)
> >>              result = func(*args, **kwargs)
> >>              print "returning %s from %s" % (result, func.func_name)
> >>              return result
> >>          return show
>
> > Sure, but during runtime, pass_decorator will still be called, even
> > though it doesn't do anything. I am wondering if I can disable this
> > additional function call at non-debug runtime. In other words, I want
> > the calling stack to contain nodecoratorat all, not even

> > pass_decorator.
>
> > I should mention that, in my code, the symbol "decorator" is imported
> > from Michele Simionato'sdecorator.py file.

>
> pass_decorator will be called when the decorated function is _defined_,
> but not when the decorated function is _called_.

Why is it then that during runtime, with a breakpoint in some
arbitrary main() in main.py, I get something similar to the following
call stack:

main.py, line xxx, in <module>
main()

<string>, line 2, in main

decorator.py, line 261, in pass_decorator
return f(*args, **kw)

main.py, line yyy, in main()
* breakpoint line here *

It looks to me the call stack still includes the additional level of
the decorator... what am I missing? Thank you for your time.

MRAB

unread,
Mar 31, 2010, 5:27:05 PM3/31/10
to pytho...@python.org

Are you still defining your decorators in the same way as in your
original post?

A decorator shouldn't call the function it's decorating.

My version of pass_decorator just returns the function it's decorating,
not call it, and my version of trace_decorator returns a locally-defined
function which will call the decorated function.

Stephen Hansen

unread,
Mar 31, 2010, 5:28:27 PM3/31/10
to
On 2010-03-31 13:59:01 -0700, LX said:
>> pass_decorator will be called when the decorated function is _defined_,
>> but not when the decorated function is _called_.
>
> Why is it then that during runtime, with a breakpoint in some
> arbitrary main() in main.py, I get something similar to the following
> call stack:
>
> main.py, line xxx, in <module>
> main()
>
> <string>, line 2, in main
>
> decorator.py, line 261, in pass_decorator
> return f(*args, **kw)
>
> main.py, line yyy, in main()
> * breakpoint line here *
>
> It looks to me the call stack still includes the additional level of
> the decorator... what am I missing? Thank you for your time.

You're not defining pass_decorator correctly. Always show us the actual
code when you're showing results.

Your pass_decorator should just return the original function,
undecorated, unmodified if you're not in debug. It should only return a
decorated function otherwise.

Consider:

-----
from decorator import decorator

def debug_decorator(fn):
if __debug__:
def wrapper(fn, *args, **kwargs):
# insert pre-condition testing here
if len(args) != 1:
raise ValueError("Incorrect number of arguments")

result = fn(*args, **kwargs)

# insert post-condition testing here
if result not in (True, False):
raise ValueError("Incorrect return value!")

return result
return decorator(wrapper, fn)
else:
return fn

@debug_decorator
def my_test(arg):
if not arg:
raise RuntimeError
return True

my_test(0)
-----

And the output depending on if you're in debug mode or not:
Top:test ixokai$ python deco.py
Traceback (most recent call last):
File "deco.py", line 27, in <module>
my_test(0)
File "<string>", line 2, in my_test
File "deco.py", line 10, in wrapper
result = fn(*args, **kwargs)
File "deco.py", line 24, in my_test
raise RuntimeError
RuntimeError

Top:test ixokai$ python -O deco.py
Traceback (most recent call last):
File "deco.py", line 27, in <module>
my_test(0)
File "deco.py", line 24, in my_test
raise RuntimeError
RuntimeError


--
--S

... p.s: change the ".invalid" to ".com" in email address to reply privately.

Steven D'Aprano

unread,
Mar 31, 2010, 6:53:51 PM3/31/10
to
On Wed, 31 Mar 2010 22:27:05 +0100, MRAB wrote:

> LX wrote:
[...]


>> It looks to me the call stack still includes the additional level of
>> the decorator... what am I missing? Thank you for your time.
>
> Are you still defining your decorators in the same way as in your
> original post?
>
> A decorator shouldn't call the function it's decorating.

*raises eyebrow*

Surely, in the general case, a decorator SHOULD call the function it is
decorating? I'm sure you know that, but your wording is funny and could
confuse the OP.

In this specific case, where the OP wants a "do nothing pass-decorator",
he should do this:

def decorator(func):
if __debug__:
...
else:
return func

rather than this:

def decorator(func):
if __debug__:
...
else:
def inner(*args):
return func(*args)
return inner

--
Steven

MRAB

unread,
Mar 31, 2010, 7:27:51 PM3/31/10
to pytho...@python.org
Steven D'Aprano wrote:
> On Wed, 31 Mar 2010 22:27:05 +0100, MRAB wrote:
>
>> LX wrote:
> [...]
>>> It looks to me the call stack still includes the additional level of
>>> the decorator... what am I missing? Thank you for your time.
>> Are you still defining your decorators in the same way as in your
>> original post?
>>
>> A decorator shouldn't call the function it's decorating.
>
> *raises eyebrow*
>
> Surely, in the general case, a decorator SHOULD call the function it is
> decorating? I'm sure you know that, but your wording is funny and could
> confuse the OP.
>
What I mean is that the function that's doing the decorating shouldn't
call the function; it's the locally-defined wrapper function that calls
the decorated function.

For example, in my version of trace_decorator() it's show() that calls
the decorated function, not trace_decorator() itself.

Unless the word 'decorator' refers to the locally-defined function, in
which case, what do you call the function that does the wrapping, the
one whose name follows the '@'?

LX

unread,
Mar 31, 2010, 8:01:05 PM3/31/10
to
On Mar 31, 2:28 pm, Stephen Hansen <apt.shan...@gmail.invalid> wrote:
> On 2010-03-31 13:59:01 -0700, LX said:
>
>
>
>
>
> >> pass_decorator will be called when the decorated function is _defined_,
> >> but not when the decorated function is _called_.
>
> > Why is it then that during runtime, with a breakpoint in some
> > arbitrary main() in main.py, I get something similar to the following
> > call stack:
>
> > main.py, line xxx, in <module>
> >  main()
>
> > <string>, line 2, in main
>
> >decorator.py, line 261, in pass_decorator
> >  return f(*args, **kw)
>
> > main.py, line yyy, in main()
> >  * breakpoint line here *
>
> > It looks to me the call stack still includes the additional level of
> > thedecorator... what am I missing? Thank you for your time.

>
> You're not defining pass_decorator correctly. Always show us the actual
> code when you're showing results.
>
> Your pass_decorator should just return the original function,
> undecorated, unmodified if you're not in debug. It should only return a
> decorated function otherwise.
>
> Consider:
>
> -----
> fromdecoratorimportdecorator
>
> def debug_decorator(fn):
>     if __debug__:
>         def wrapper(fn, *args, **kwargs):
>             # insert pre-condition testing here
>             if len(args) != 1:
>                 raise ValueError("Incorrect number of arguments")
>
>             result = fn(*args, **kwargs)
>
>             # insert post-condition testing here
>             if result not in (True, False):
>                 raise ValueError("Incorrect return value!")
>
>             return result
>         returndecorator(wrapper, fn)

Thank you all a lot! The last 3 posts helped me figure it out. Indeed,
my pass_decorator function used "@decorator", using the decoratory.py
library. This prevented return of the original function. The code that
I have now is:

#NOT THIS:
#@decorator
#def pass_decorator(f, *args, **kw): # what about the slow-down that
incurs when using pass_decorator
#return f(*args, **kw)

def pass_decorator(f):
return f

Steven D'Aprano

unread,
Mar 31, 2010, 9:30:21 PM3/31/10
to
On Thu, 01 Apr 2010 00:27:51 +0100, MRAB wrote:

>>> A decorator shouldn't call the function it's decorating.
>>
>> *raises eyebrow*
>>
>> Surely, in the general case, a decorator SHOULD call the function it is
>> decorating? I'm sure you know that, but your wording is funny and could
>> confuse the OP.
>>
> What I mean is that the function that's doing the decorating shouldn't
> call the function; it's the locally-defined wrapper function that calls
> the decorated function.

Ah, gotcha, that makes sense. Now I understand the distinction you were
making. Thank you for the clarification.

--
Steven

MRAB

unread,
Apr 1, 2010, 8:54:49 AM4/1/10
to pytho...@python.org
I had the following idea: define the terms 'decorator', 'decoration' and
'decoratee'. The decorator applies the decoration to the decoratee. The
decoratee is the function defined locally in the decorator.

Steve Holden

unread,
Apr 1, 2010, 9:16:29 AM4/1/10
to pytho...@python.org
MRAB wrote:
> I had the following idea: define the terms 'decorator', 'decoration' and
> 'decoratee'. The decorator applies the decoration to the decoratee. The
> decoratee is the function defined locally in the decorator.

It would make more sense (to me, at least) if the decoratee were the
function passed as an argument to the decorator.

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
See PyCon Talks from Atlanta 2010 http://pycon.blip.tv/
Holden Web LLC http://www.holdenweb.com/
UPCOMING EVENTS: http://holdenweb.eventbrite.com/

Steve Howell

unread,
Apr 1, 2010, 10:07:53 AM4/1/10
to
On Apr 1, 6:16 am, Steve Holden <st...@holdenweb.com> wrote:

> MRAB wrote:
>
> > I had the following idea: define the terms 'decorator', 'decoration' and
> > 'decoratee'. The decorator applies the decoration to the decoratee. The
> > decoratee is the function defined locally in the decorator.
>
> It would make more sense (to me, at least) if the decoratee were the
> function passed as an argument to the decorator.
>

Me too. I do like the idea of coming up with a consistent terminology.

MRAB

unread,
Apr 1, 2010, 1:01:36 PM4/1/10
to pytho...@python.org
Steve Holden wrote:
> MRAB wrote:
>> Steven D'Aprano wrote:
>> I had the following idea: define the terms 'decorator', 'decoration' and
>> 'decoratee'. The decorator applies the decoration to the decoratee. The
>> decoratee is the function defined locally in the decorator.
>
> It would make more sense (to me, at least) if the decoratee were the
> function passed as an argument to the decorator.
>
Oops, you're right! What I meant was that the _decoration_ is the
0 new messages