Hopefully anyone is still reading python-dev.

I'm going to try to summarize the differences between the two proposals,
even though Mark already did so in his PEP. But I'd like to start by
calling out the key point of contention.

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

My first reaction was to propose to drop this feature, but I realize it's
kind of important for debuggers to be able to execute arbitrary code in
function code -- assignments to locals should affect the frame, but it
should also be possible to create new variables (e.g. temporaries). So I
agree we should keep this.

Terminology-wise, I will refer to variables that are allocated in the frame
(like "x" above, and including nonlocals/cells) as "proper" variables.

Both PEPs give up when it comes to locals(), declaring it to return a
snapshot in this case. This is mostly to ensure better backwards
compatibility, since existing code calling locals() may well assume it's a
dict. Both PEPs make f_locals some kind of proxy that gives a direct
read-write view on the variables in the frame (including cells used for
nonlocal references), but they differ in the precise semantics.

So apparently the key difference of opinion between Mark and Nick is about
f_locals, and what to do with extras. In Nick's proposal when you reference
f.f_locals twice in a row (for the same frame object f), you get the same
proxy object, whereas in Mark's proposal you get a different object each
time, but it doesn't matter, because the proxy has no state other than a
reference to the frame. In Mark's proposal, if you assign a value to an
extra variable, it gets stored in a hidden dict field on the frame, and
when you read the proxy, the contents of that hidden dict field gets
included. This hidden dict lazily created on the first store to an extra
variable. (Mark shows pseudo-code to clarify this; the hidden dict is
stored as _extra_locals on the frame.)

In Nick's proposal, there's a cache on the frame that stores both the
extras and the proper variables. This cache can get out of sync with the
contents of the proper variables when some bytecode is executed (for
performance reasons we don't want the bytecode to keep the cache up to date
on every store), so there's an operation to sync the frame cache
(sync_frame_cache(), it's not defined in which namespace this exists -- is
it a builtin or in sys?).

Frankly the description in Nick's PEP is hard to follow -- I am not 100%
sure what is meant by "the dynamic snapshot", and it's not quite clear
whether proper variables are copied into the cache (and if so, why).

There are also differences in the proposed C API changes, but the
differences there are solvable once we choose the semantics for f_locals.

Personally, I find Mark's proposed semantics for f_locals simpler --
there's no cache, only storage for extras, so there's nothing that can get
out of sync.

I would even consider making locals() return the same proxy -- this is
simpler and more consistent with module and class scopes, but it is less
backwards compatible, and locals() is used orders of magnitude more than
f_locals. (Also, we'd have to modify exec() and eval() to allow using a
non-dict as globals, which would require some deep changes in the
interpreter.)

--Guido

PS. In Mark's PEP, there's a pseudo-code version of locals() that can give
a different result in class scope than the current CPython implementation:
Using __prepare__, a metaclass can provide a namespace to execute the class
body that's not a dict (subclass) instance. The current CPython behavior
and AFAICT Nick's PEP return that namespace from locals(), but Mark's
pseudo-code would return a snapshot copy. I think it's better to stick to
the current semantics (and I suspect Mark overlooked this edge case).

On Fri, Aug 20, 2021 at 8:23 AM Mark Shannon <m...@hotpy.org> wrote:

> Hi all,
>
> I have submitted PEP 667 as an alternative to PEP 558.
> https://www.python.org/dev/peps/pep-0667/
>
> Nick and I have agreed to disagree on the way to fix locals() and
> f_locals. We are both in agreement that it needs fixing.
>
> In summary, PEP 667 has roughly the same surface behavior as PEP 558 but
> is simpler and more consistent internally, at the expense of some minor
> C API backwards incompatibility issues.
>
> PEP 558 also has backwards incompatibility issues, but claims to be more
> compatible at the C API level.
>
> Cheers,
> Mark.
> _______________________________________________
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-le...@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/4RH5YCXIHIP6MRVTCOKOOO4GKCIMH4GJ/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*
<http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/>
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@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/ACFNKD4TGTEXUP45QGDKSR24KVAE5YJF/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to