Stagnant Frame Data?

Mike loftusoft at yahoo.com
Sun Nov 15 10:13:41 EST 2009


I'll apologize first for this somewhat lengthy example. It does
however recreate the problem I've run into. This is stripped-down code
from a much more meaningful system.

I have two example classes, "AutoChecker" and "Snapshot" that evaluate
variables in their caller's namespace using the frame stack. As
written, the output is not what is expected: the variables evaluate to
"stagnant" values.

However, if the one indicated line is uncommented, then the result is
as expected.

So my questions are: Is this a bug in Python? Is this an invalid use
of frame data? Why does the single line "sys._getframe(1).f_locals"
fix the behavior?

Thanks,

Mike


import sys

class Snapshot(object):
    def __init__(self, caller_globals, caller_locals):
        self.signals = []
        self.caller_globals = caller_globals
        self.caller_locals = caller_locals

    def get_values(self):
        samples = {}
        for signal in self.signals:
            samples[signal] = eval(signal, self.caller_globals,
self.caller_locals)
        return samples

    def print_values(self):
        print 'snapshot data'
        for name, value in self.get_values().items():
            print '\t', name, '=', value


class AutoChecker(object):
    def __init__(self, statement):
        self.statement = statement
        self.caller_globals = sys._getframe(1).f_globals
        self.caller_locals = sys._getframe(1).f_locals
        self.snapshot = Snapshot(self.caller_globals,
self.caller_locals)
        self.snapshot_history = []

    def check(self):
        # uncomment following line to get expected behavior
        #sys._getframe(1).f_locals
        if eval(self.statement, self.caller_globals,
self.caller_locals) == False:
            print self.statement, 'failed'
            self.snapshot.print_values()
            self.snapshot_history.append(self.snapshot.get_values())

    def report(self):
        if len(self.snapshot_history):
            return
        print 'snapshot history'
        for samples in self.snapshot_history:
            for name, value in samples.items():
                print '\t', name, '=', value


def f():
    x = 0.0
    y = 0.0
    ac1 = AutoChecker('x < 2.0')
    ac1.snapshot.signals.append('x')
    ac1.snapshot.signals.append('y')

    for i in range(5):
        x = i / 2.0
        y = x / 2
        print i, x
        ac1.check()
        ac1.snapshot.print_values()
    ac1.report()



More information about the Python-list mailing list