-
-
Save techtonik/2151727 to your computer and use it in GitHub Desktop.
# Public Domain, i.e. feel free to copy/paste | |
# Considered a hack in Python 2 | |
import inspect | |
def caller_name(skip=2): | |
"""Get a name of a caller in the format module.class.method | |
`skip` specifies how many levels of stack to skip while getting caller | |
name. skip=1 means "who calls me", skip=2 "who calls my caller" etc. | |
An empty string is returned if skipped levels exceed stack height | |
""" | |
stack = inspect.stack() | |
start = 0 + skip | |
if len(stack) < start + 1: | |
return '' | |
parentframe = stack[start][0] | |
name = [] | |
module = inspect.getmodule(parentframe) | |
# `modname` can be None when frame is executed directly in console | |
# TODO(techtonik): consider using __main__ | |
if module: | |
name.append(module.__name__) | |
# detect classname | |
if 'self' in parentframe.f_locals: | |
# I don't know any way to detect call from the object method | |
# XXX: there seems to be no way to detect static method call - it will | |
# be just a function call | |
name.append(parentframe.f_locals['self'].__class__.__name__) | |
codename = parentframe.f_code.co_name | |
if codename != '<module>': # top level usually | |
name.append( codename ) # function or a method | |
del parentframe | |
return ".".join(name) |
__qname__
sounds better to me, but it still it doesn't solve the problem of orientation in the frame completely. For example, what is the convention to identify the top level? I understand that this top level differs depending on how the code is called - usual 'python module.py', console, exec() - what else is possible? What is the difference? Why it should be different? These questions I don't have answers for now.
Very useful, thank you.
Thanks!
👏 thanks so much for this!
6 years later, but still want to say thanks!
Thanks very much! This is a very useful function!
I have made one minor change to line 14. I noticed that when I was using this function to set the name of a logger at many points in my program, things seemed to start running pretty slowly. After profiling, it looks like this is due to line 14 (i.e., the call to inspect.stack
). Fortunately, a simplified function can be used instead, as shown below:
...
>>> import sys
>>> import inspect
>>>
>>> def stack_(frame):
... framelist = []
... while frame:
... framelist.append(frame)
... frame = frame.f_back
... return framelist
...
>>> %timeit stack_(sys._getframe(1))
100000 loops, best of 3: 3.09 µs per loop
>>> %timeit inspect.stack()
100 loops, best of 3: 8.36 ms per loop
While the simple stack_
function is not a drop-in replacement for inspect.stack
, it provides all the functionality required for caller_name
and is over 2000x faster.
Here is a modified version of the code, including an additional function _L
which can be used to return a logger named based on the current method/function context:
# Public Domain, i.e. feel free to copy/paste
# Considered a hack in Python 2
import inspect
import logging
import sys
def _L(skip=0):
'''Shorthand to get logger for current function frame.'''
return logging.getLogger(caller_name(skip + 1))
def caller_name(skip=2):
"""Get a name of a caller in the format module.class.method
`skip` specifies how many levels of stack to skip while getting caller
name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.
An empty string is returned if skipped levels exceed stack height
"""
def stack_(frame):
framelist = []
while frame:
framelist.append(frame)
frame = frame.f_back
return framelist
stack = stack_(sys._getframe(1))
start = 0 + skip
if len(stack) < start + 1:
return ''
parentframe = stack[start]
name = []
module = inspect.getmodule(parentframe)
# `modname` can be None when frame is executed directly in console
# TODO(techtonik): consider using __main__
if module:
name.append(module.__name__)
# detect classname
if 'self' in parentframe.f_locals:
# I don't know any way to detect call from the object method
# XXX: there seems to be no way to detect static method call - it will
# be just a function call
name.append(parentframe.f_locals['self'].__class__.__name__)
codename = parentframe.f_code.co_name
if codename != '<module>': # top level usually
name.append(codename) # function or a method
del parentframe
return ".".join(name)
thanks!!!
Think you,it's very useful~
I've never used Python before, is this too hard to use?
This is exactly why I was overjoyed when http://www.python.org/dev/peps/pep-3155/ appeared. Python 3.3+ only, though.
Otherwise the only sure way to figure out the class name I can think of would be using
parentframe.f_code.co_filename
andco_firstlineno
, then parsing the source file (if it's available) and seeing what classes where defined in which code lines.