FAQ
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()

Search Discussions

  • Peter Otten at Nov 15, 2009 at 4:00 pm

    Mike wrote:

    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?
    A simplified demonstration of your problem:
    def f(update):
    ... a = locals()
    ... x = 42
    ... if update: locals()
    ... print a
    ...
    f(False)
    {'update': False}
    f(True)
    {'a': {...}, 'x': 42, 'update': True}

    The local namespace is not a dictionary, and the locals()/f_locals
    dictionary contains a snapshot of the local namespace. Accessing the
    f_locals attribute is one way to trigger an update of that snapshot.

    What's puzzling is that the same dictionary is reused.

    Peter
  • Exarkun at Nov 15, 2009 at 4:19 pm

    On 04:00 pm, __peter__ at web.de wrote:
    Mike wrote:
    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?
    A simplified demonstration of your problem:
    def f(update):
    ... a = locals()
    ... x = 42
    ... if update: locals()
    ... print a
    ...
    f(False)
    {'update': False}
    f(True)
    {'a': {...}, 'x': 42, 'update': True}

    The local namespace is not a dictionary, and the locals()/f_locals
    dictionary contains a snapshot of the local namespace. Accessing the
    f_locals attribute is one way to trigger an update of that snapshot.

    What's puzzling is that the same dictionary is reused.
    http://bugs.python.org/issue6116 is vaguely related.

    Jean-Paul
  • Terry Reedy at Nov 15, 2009 at 6:36 pm

    Peter Otten wrote:
    Mike wrote:
    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?
    No. The existence and use of sys._getframe -- notice the leading
    underscore -- is a CPython implementation artifact. Use at your own risk.
    Is this an invalid use of frame data?
    Up to you to decide.
    Why does the single line "sys._getframe(1).f_locals"
    fix the behavior?
    It updates the dictionary.
    A simplified demonstration of your problem:
    def f(update):
    ... a = locals()
    ... x = 42
    ... if update: locals()
    ... print a
    ...
    f(False)
    {'update': False}
    f(True)
    {'a': {...}, 'x': 42, 'update': True}

    The local namespace is not a dictionary, and the locals()/f_locals
    dictionary contains a snapshot of the local namespace. Accessing the
    f_locals attribute is one way to trigger an update of that snapshot.

    What's puzzling is that the same dictionary is reused.
    Efficiency? Consistency? The doc for locals says "locals()
    Update and return a dictionary representing the current local symbol
    table." In class statements, where (currently) the local namespace *is*
    a dict, no update is needed and locals() simply returns the dict, the
    same one each time.

    Terry Jan Reedy
  • Mike at Nov 18, 2009 at 10:10 am

    On Nov 15, 1:36?pm, Terry Reedy wrote:
    Peter Otten wrote:
    Mike wrote:
    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?
    No. The existence and use of sys._getframe -- notice the leading
    underscore -- is a CPython implementation artifact. Use at your own risk.

    ?>> Is this an invalid use of frame data?

    Up to you to decide.

    ?>> Why does the single line "sys._getframe(1).f_locals"
    fix the behavior?
    It updates the dictionary.


    A simplified demonstration of your problem:
    def f(update):
    ... ? ? a = locals()
    ... ? ? x = 42
    ... ? ? if update: locals()
    ... ? ? print a
    ...
    f(False)
    {'update': False}
    f(True)
    {'a': {...}, 'x': 42, 'update': True}
    The local namespace is not a dictionary, and the locals()/f_locals
    dictionary contains a snapshot of the local namespace. Accessing the
    f_locals attribute is one way to trigger an update of that snapshot.
    What's puzzling is that the same dictionary is reused.
    Efficiency? Consistency? The doc for locals says "locals()
    Update and return a dictionary representing the current local symbol
    table." In class statements, where (currently) the local namespace *is*
    a dict, no update is needed and locals() simply returns the dict, the
    same one each time.

    Terry Jan Reedy
    Thanks for the replies.

    If Peter's concise and much-appreciated example were to be modified as
    such:
    def f(update):
    .....: a = inspect.stack()[0][0].f_locals
    .....: x = 42
    .....: if update:
    .....: inspect.stack()[0][0].f_locals
    .....: print a
    .....:
    f(False)
    {'update': False}
    f(True)
    {'a': {...}, 'x': 42, 'update': True}

    Now there's no use of locals() to perform its documented "Update", but
    just a simple access of a dictionary. This behavior is curious.

    Further modification by accessing 'a' instead of '...f_locals' upon
    update produces:
    f(False)
    {'update': False}
    f(True)
    {'update': True}

    Checking the id() of 'a' and '...f_locals' shows the same "address".

    How is it that '...f_locals' gets updated in this example? Is it done
    in "stack()"?

    Where exactly is the frame data stored prior to updating the
    dictionary and can I get to it?

    Thanks,
    Mike
  • Mike at Nov 18, 2009 at 10:10 am

    On Nov 15, 1:36?pm, Terry Reedy wrote:
    Peter Otten wrote:
    Mike wrote:
    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?
    No. The existence and use of sys._getframe -- notice the leading
    underscore -- is a CPython implementation artifact. Use at your own risk.

    ?>> Is this an invalid use of frame data?

    Up to you to decide.

    ?>> Why does the single line "sys._getframe(1).f_locals"
    fix the behavior?
    It updates the dictionary.


    A simplified demonstration of your problem:
    def f(update):
    ... ? ? a = locals()
    ... ? ? x = 42
    ... ? ? if update: locals()
    ... ? ? print a
    ...
    f(False)
    {'update': False}
    f(True)
    {'a': {...}, 'x': 42, 'update': True}
    The local namespace is not a dictionary, and the locals()/f_locals
    dictionary contains a snapshot of the local namespace. Accessing the
    f_locals attribute is one way to trigger an update of that snapshot.
    What's puzzling is that the same dictionary is reused.
    Efficiency? Consistency? The doc for locals says "locals()
    Update and return a dictionary representing the current local symbol
    table." In class statements, where (currently) the local namespace *is*
    a dict, no update is needed and locals() simply returns the dict, the
    same one each time.

    Terry Jan Reedy
    Thanks for the replies.

    If Peter's concise and much-appreciated example were to be modified as
    such:
    def f(update):
    .....: a = inspect.stack()[0][0].f_locals
    .....: x = 42
    .....: if update:
    .....: inspect.stack()[0][0].f_locals
    .....: print a
    .....:
    f(False)
    {'update': False}
    f(True)
    {'a': {...}, 'x': 42, 'update': True}

    Now there's no use of locals() to perform its documented "Update", but
    just a simple access of a dictionary. This behavior is curious.

    Further modification by accessing 'a' instead of '...f_locals' upon
    update produces:
    f(False)
    {'update': False}
    f(True)
    {'update': True}

    Checking the id() of 'a' and '...f_locals' shows the same "address".

    How is it that '...f_locals' gets updated in this example? Is it done
    in "stack()"?

    Where exactly is the frame data stored prior to updating the
    dictionary and can I get to it?

    Thanks,
    Mike

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
grouppython-list @
categoriespython
postedNov 15, '09 at 3:13p
activeNov 18, '09 at 10:10a
posts6
users4
websitepython.org

People

Translate

site design / logo © 2022 Grokbase