Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #80853 > unrolled thread

frame.f_locals['__class__'] -- When does it (not) exist and why?

Started bykevinarpe@gmail.com
First post2014-11-15 21:25 -0800
Last post2014-11-15 22:28 -0800
Articles 3 — 2 participants

Back to article view | Back to comp.lang.python


Contents

  frame.f_locals['__class__'] -- When does it (not) exist and why? kevinarpe@gmail.com - 2014-11-15 21:25 -0800
    Re: frame.f_locals['__class__'] -- When does it (not) exist and why? Chris Angelico <rosuav@gmail.com> - 2014-11-16 16:40 +1100
      Re: frame.f_locals['__class__'] -- When does it (not) exist and why? kevinarpe@gmail.com - 2014-11-15 22:28 -0800

#80853 — frame.f_locals['__class__'] -- When does it (not) exist and why?

Fromkevinarpe@gmail.com
Date2014-11-15 21:25 -0800
Subjectframe.f_locals['__class__'] -- When does it (not) exist and why?
Message-ID<c1287648-21e0-457b-ac41-010642a41e9c@googlegroups.com>
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

[toc] | [next] | [standalone]


#80856

FromChris Angelico <rosuav@gmail.com>
Date2014-11-16 16:40 +1100
Message-ID<mailman.15870.1416116415.18130.python-list@python.org>
In reply to#80853
On Sun, Nov 16, 2014 at 4:25 PM,  <kevinarpe@gmail.com> wrote:
> 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.)
>

Interesting. Are you sure you can't get the class name via the
function, rather than depending on this magic local? That seems
fragile.

Your example code isn't currently runnable ("def class"); can you
provide a self-contained program that actually attempts to display
something? It'd be helpful for those of us who aren't familiar with
internal and esoteric details of tracebacks and __init__ :)

ChrisA

[toc] | [prev] | [next] | [standalone]


#80857

Fromkevinarpe@gmail.com
Date2014-11-15 22:28 -0800
Message-ID<bb97d169-0555-411b-97cf-7b756d8d645e@googlegroups.com>
In reply to#80856
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()

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web