Question

a.py

import d

d.funcme('blah')

d.py

import sys
import Errors
def argcheck(in_=(), out=(type(None),)):
    def _argcheck(function):
        # do something here
        def __argcheck(*args, **kw):
            print '+++++++++ checking types before calling the func'
            # do something here
            res = function(*args, **kw)
            return res
        return __argcheck
    return _argcheck

@argcheck((str))       <-----
def funcme(name):
    try:
        f = sys._getframe(1)
    except ValueError, err:
        raise Errors.UserError(err)     # stack too deep
        filename, lineno = f.f_globals['__name__'], f.f_lineno
    print filename, lineno

OUTPUT without argcheck decorator (comment out the @argcheck((str))):

$ python a.py  
__main__ 3  

OUTPUT with argcheck decorator:

$ python a.py  
+++++++++ checking types before calling the func  
defines 9  

Questions:

  1. What's decorator doing so that it's changing the values for _getframe?

  2. How can I preserve the information so it captures the original information i.e __main__ 3 and not defines 9?

Was it helpful?

Solution

The problem is that yourfuncme()function is assuming it has been called directly rather indirectly through something else — such as a decorator. This could be fixed by changing its calling sequence and adding an additionaldepthkeyword argument with a default value which will be passed on to _sys._getframe(). With this scaffolding in place, the decorator can then override the default value. The following will print the same thing whether or not the decorator has been applied:

 1 import sys
 2 import Errors
 3 def argcheck(in_=(), out=(type(None),)):
 4     def _argcheck(function):
 5         # do something here
 6         def __argcheck(*args, **kw):
 7             print '+++++++++ checking types before calling the func'
 8             # do something here
 9             res = function(*args, depth=2, **kw)  # override default depth
10             return res
11         return __argcheck
12     return _argcheck
13
14 @argcheck((str))
15 def funcme(name, depth=1):  # added keyword arg with default value
16     try:
17         f = sys._getframe(depth)   # explicitly pass stack depth wanted
18     except ValueError, err:
19         raise Errors.UserError(err)     # stack too deep
20
21     filename, lineno = f.f_globals['__name__'], f.f_lineno
22     print filename, lineno

OTHER TIPS

Decorator is basically syntactic sugar. This:

@argcheck((str))
def funcme(name):

is the same as this:

funcme = argcheck(str)(funcme)

Now you can see why decorators change the call stack.

I am not sure how to work around this in arbitrary cases, but if you know in advance something about the decorators you could perhaps compensate your code for it. You might also look into functools.wraps, perhaps that would provide some clues that might help.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top