The callerframe decorator¶
The callerframe decorator adds to the decorated function information about it’s caller. This information can be accessed through the __caller_frame__ attribute, which is inserted into the function’s globals. The information is the namedtuple FrameInfo containing:
frame: the caller’s framefilename: the filenameline_number: the line numberfunction_name: the function namecontext: a list of context linesindex: the index of the line in thecontextwhere the call is done
Motivation¶
Suppose you want to define a log() function:
>>> def log(kind, message):
... print("{}: {}".format(kind, message))
...
>>> log("error", "lost connection")
error: lost connection
>>>
You may want to automatically add to the log some information about the caller, for instance:
>>> def log(kind, message):
... print("{}: function {}: {}".format(kind, function_name, message))
so you need to obtain the callers’ function name, and eventually the filename or the line number.
Using inspect you can easily obtain such information:
>>> import inspect
>>> def log(kind, message):
... frame, filename, line_number, function_name, context, index = inspect.getouterframes(inspect.currentframe())[1]
... print("{}: function {}: {}".format(kind, function_name, message))
...
>>> def foo():
... log("error", "lost connection")
...
>>> foo()
error: function foo: lost connection
But what if you want to define also an error function using the log one? In this cast log should show information about the error‘s caller,
and not about it’s direct caller:
>>> def error(message):
... log("error", message)
...
>>> def bar():
... error("lost connection")
...
>>> bar()
error: function error: lost connection
Notice that the log() function should behave as above when called directly, showing its direct caller’s name. But it should show
the error() caller’s name when called through error().
Solution¶
The callerframe decorator can solve these problems. First of all, it adds to the decorated function’s globals a __caller_frame__
attribute containing all the information about the caller:
>>> @callerframe
... def log(kind, message):
... print("{}: function {}: {}".format(kind, __caller_frame__.function_name, message))
...
>>> def foo():
... log("error", "lost connection")
...
>>> foo()
error: function foo: lost connection
Moreover, it is possible to use the same decorator for the error function too: in this case, when called through error, log will
receive the error‘s caller:
>>> @callerframe
... def error(message):
... log("error", message)
...
>>> def bar():
... error("lost connection")
...
>>> bar()
error: function bar: lost connection
In general, the first decorated function in a call stack sets the caller’s information.
It is possible to change the name of the added attribute:
>>> @callerframe("CALLERFRAME")
... def show_caller():
... print(CALLERFRAME.function_name)
...
>>> def foo():
... show_caller()
...
>>> foo()
foo
Contents: