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 thecontext
where 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:
callerframe package¶
Module contents¶
The callerframe decorator adds a __caller_frame__ global attribute to the decorated function’s globals; this attribute refers to a FrameInfo object containing information about the caller function:
- frame: the caller’s frame;
- filename: the name of the file where the function has been called;
- line_number: the line number of the call in filename;
- function_name: the name of the caller function;
- context: a list of source line containing the call;
- index: the index of the line in context where the function has been called.
>>> @callerframe
... def log(kind, message):
... print("{}: function {}: {}".format(kind, __caller_frame__.function_name, message))
...
>>> def foo():
... log("error", "lost connection")
...
>>> def main():
... return foo()
...
>>> main()
error: function foo: lost connection
The log function receives information about it’s direct caller; but what if we want to have an error() function based on log()?
>>> def error(message):
... log("error", message)
...
>>> def foo():
... error("lost connection")
...
>>> def main():
... return foo()
...
>>> main()
error: function error: lost connection
This is correct, since error() is the direct caller of the log() function; nevertheless we would like to show the information about the error()’s caller instead. In this case it is possible to decorate error() too (no modification is needed in log):
>>> @callerframe
... def error(message):
... log("error", message)
...
>>> def foo():
... error("lost connection")
...
>>> def main():
... return foo()
...
>>> main()
error: function foo: lost connection
In other words, the first decorated function found in the call stack sets the caller information. This information is not overwritten by nested calls to decorated functions.
The attribute name, by default __caller_frame__, can be choosen:
>>> @callerframe("CALLERFRAME")
... def show_caller():
... print(CALLERFRAME.function_name)
...
>>> def foo():
... show_caller()
...
>>> foo()
foo
-
class
callerframe.
FrameInfo
(frame, filename, line_number, function_name, context, index)¶ Bases:
tuple
-
__getnewargs__
()¶ Return self as a plain tuple. Used by copy and pickle.
-
__getstate__
()¶ Exclude the OrderedDict from pickling
-
static
__new__
(_cls, frame, filename, line_number, function_name, context, index)¶ Create new instance of FrameInfo(frame, filename, line_number, function_name, context, index)
-
__repr__
()¶ Return a nicely formatted representation string
-
context
¶ Alias for field number 4
-
filename
¶ Alias for field number 1
-
frame
¶ Alias for field number 0
-
function_name
¶ Alias for field number 3
-
index
¶ Alias for field number 5
-
line_number
¶ Alias for field number 2
-