Stagnant Frame Data?

M

Mike

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

Peter Otten

Mike said:
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:
.... a = locals()
.... x = 42
.... if update: locals()
.... print a
....{'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
 
E

exarkun

A simplified demonstration of your problem:
... a = locals()
... x = 42
... if update: locals()
... print a
...
{'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
 
T

Terry Reedy

No. The existence and use of sys._getframe -- notice the leading
underscore -- is a CPython implementation artifact. Use at your own risk.

Up to you to decide.

It updates the dictionary.
A simplified demonstration of your problem:

... a = locals()
... x = 42
... if update: locals()
... print a
...
{'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
 
M

Mike

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"


It updates the dictionary.






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:
.....: a = inspect.stack()[0][0].f_locals
.....: x = 42
.....: if update:
.....: inspect.stack()[0][0].f_locals
.....: print a
.....:{'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:
{'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
 
M

Mike

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"


It updates the dictionary.






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:
.....: a = inspect.stack()[0][0].f_locals
.....: x = 42
.....: if update:
.....: inspect.stack()[0][0].f_locals
.....: print a
.....:{'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:
{'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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top