Stagnant Frame Data?

Discussion in 'Python' started by Mike, Nov 15, 2009.

  1. Mike

    Mike Guest

    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()
     
    Mike, Nov 15, 2009
    #1
    1. Advertising

  2. Mike

    Peter Otten Guest

    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
     
    Peter Otten, Nov 15, 2009
    #2
    1. Advertising

  3. Mike

    Guest

    On 04:00 pm, 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
     
    , Nov 15, 2009
    #3
  4. Mike

    Terry Reedy Guest

    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
     
    Terry Reedy, Nov 15, 2009
    #4
  5. Mike

    Mike Guest

    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, Nov 18, 2009
    #5
  6. Mike

    Mike Guest

    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, Nov 18, 2009
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Søren M. Olesen
    Replies:
    0
    Views:
    611
    Søren M. Olesen
    Nov 13, 2003
  2. Microsoft
    Replies:
    3
    Views:
    719
    Hugo Wetterberg
    Oct 4, 2004
  3. JAW
    Replies:
    2
    Views:
    595
  4. jason
    Replies:
    1
    Views:
    1,359
    Damien
    Sep 5, 2005
  5. Kathryn
    Replies:
    4
    Views:
    386
Loading...

Share This Page