I apologize. This does indeed not work. What does work, however, is setting a variable from a tracing function. There is magic in the tracing machinery that writes back the variables to the frame when the tracing function returns. This causes the bug referenced as [1] in both PEPs, and both propose to fix the bug by  not writing things back that way, instead writing back whenever a key in the proxy is set. The discussion is about subtler differences between the proposals.

—Guido

On Mon, Aug 23, 2021 at 22:19 Steven D'Aprano <steve@pearwood.info> wrote:
On Sat, Aug 21, 2021 at 05:46:52PM -0700, Guido van Rossum wrote:
> Hopefully anyone is still reading python-dev.

I am :-)

[...]
> Everything here is about locals() and f_locals in *function scope*. (I use
> f_locals to refer to the f_locals field of frame objects as seen from
> Python code.) And in particular, it is about what I'll call "extra
> variables": the current CPython feature that you can add *new* variables to
> f_locals that don't exist in the frame, for example:
>
> def foo():
>     x = 1
>     locals()["y"] = 2  # or sys._getframe()["y"] = 2

I'm confused. I don't think it currently works, at least not in the
sense that I understand "works" to mean. Sure, you can add a new key to
the dict, but that doesn't add a new local variable:


    >>> def spam():
    ...     if False: y = 0  # Fool the compiler into treating y as a local.
    ...     x = 0
    ...     locals()['y'] = 1
    ...     print(sys._getframe().f_locals)
    ...     print(y)
    ...
    >>> spam()
    {'x': 0}
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in spam
    UnboundLocalError: local variable 'y' referenced before assignment


Am I missing something? The above is in 3.9, has something changed in
3.10 or am I just misunderstanding what you mean by "works"?

Using f_locals instead of locals() doesn't change the UnboundLocalError
in my testing.

If you are not referring to new local variables, then I don't understand
what these "extra variables" are or where they live in the current
implementation.

A recent thread on Discuss is maybe relevant:

https://discuss.python.org/t/how-can-i-use-exec-in-functions-in-python3-6-similar-to-python2-7/10191

Would either of these two proposals re-enable exec to work inside
functions as it used to?

I think Nick's PEP 558 does not, it wants to make it explicit that
changes to locals will not be reflected in the local variables. Although
Nick does refer to a "write back" strategy that apparently works *now*.
That suggests:

- there currently is a trick to writing new local variables in the
function namespace;

- and Nick's PEP will break it.

Am I correct?

Mark's PEP 667 says "However f.f_locals == f.f_locals will be True, and
all changes to the underlying variables, by any means, will be always be
visible" so I think that means it will allow exec to work as in
Python2.x, at least if you pass sys._getframe().f_locals to exec instead
of locals().

Perhaps we need an informational PEP to explain how local variables
inside functions work now :-)



--
Steve
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-leave@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/CREZXKZWVSICQY4WHKUPXGJU67UUPQZ2/
Code of Conduct: http://python.org/psf/codeofconduct/
--
--Guido (mobile)