frame.f_locals['__class__'] -- When does it (not) exist and why?
Hello, I am CPython 3.4+ user on Linux. I am writing a little library for myself to improve the traceback module -- print_exc() and friends. I want to include the module name, class name (if possible), and function name. Some background: traceback.print_exc() iterates through traceback objects returned by sys.exc_info()[2]. traceback.tb_frame holds each stack frame. (I call this 'frame' below.) My improved library nearly works, but I noticed a strange corner case around frame.f_locals['__class__']. When super().__init__() is called, a 'magic' local appears in frame.f_locals called '__class__'. Helpfully, it has the correct class for the context, which will differ from type(self). (I discovered this magic local by poking around in the debugger. I am unable to find any official documentation on it.) Here is the quirk: In the last class in a chain of super.__init__() calls, this magic local disappears. So I have no idea the correct class for the context. I am stuck with frame.f_locals['self']. How can I recover the correct class for the context in the last __init__() method? I noticed if I chance the last class to inherit from object, the magic local '__class__' appears again. A little code to demonstrate: # do not subclass object here def class X: def __init__(self): # frame.f_locals['__class__'] does not exist pass def class Y(X): def __init__(self): # frame.f_locals['__class__'] == Y super().__init__() def class Z(Y): def __init__(self): super().__init__() # subclass object here def class X2(object): def __init__(self): # frame.f_locals['__class__'] == X2 pass def class Y2(X2): def __init__(self): # frame.f_locals['__class__'] == Y2 super().__init__() def class Z2(Y2): def __init__(self): super().__init__() Thanks, Arpe -- https://mail.python.org/mailman/listinfo/python-list
Re: frame.f_locals['__class__'] -- When does it (not) exist and why?
Apologies for previous code example. Yes, the 'def class' should read: 'class'. Writing a sample to better demonstrate the issue made me realize that super() is doing something special. It is injecting the magic '__class__' local. I should rephrase my question: How do I get the declaring class from from a traceback object? Currently, I cannot see how to do it. Magic local '__class__' is not always available. And locals 'cls' and 'self' may come from subclasses. Code sample: import inspect import sys class X: def __init__(self): # Magic local '__class__' is missing raise ValueError() class Y(X): def __init__(self): super().__init__() class X2: def __init__(self): # Calling super() here will 'inject' magic local '__class__' super().__init__() raise ValueError() class Y2(X2): def __init__(self): super().__init__() def main(): _main(lambda: Y()) _main(lambda: Y2()) def _main(func): try: func() except: (exc_type, exc_value, traceback) = sys.exc_info() tb = traceback while tb: frame = tb.tb_frame # See: code.co_freevars. Sometimes magic '__class__' appears. code = frame.f_code lineno = frame.f_lineno func_name = code.co_name file_path = code.co_filename module = inspect.getmodule(frame, file_path) module_name = module.__name__ print("File: {}, Line: {}, Func: {}, Module: {}".format(file_path, lineno, func_name, module_name)) for name in ('__class__', 'self', 'cls'): if name in frame.f_locals: print("{}: '{}'".format(name, frame.f_locals[name])) tb = tb.tb_next print() if __name__ == '__main__': main() -- https://mail.python.org/mailman/listinfo/python-list