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:
What's decorator doing so that it's changing the values for _getframe
?
How can I preserve the information so it captures the original information i.e __main__
3 and not defines 9?
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 additionaldepth
keyword 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