Skip to content

Commit

Permalink
PEP 667: Update to account for changes to PEP 558 and feedback from N…
Browse files Browse the repository at this point in the history
…ick. (#2180)

* Remove incorrect implementation of pop(). There is an implementation in collections.MutableMapping so it is not needed. Add implementation of __contains__.

* Remove outdated section on differences with PEP 558.
  • Loading branch information
markshannon authored Dec 6, 2021
1 parent e47f924 commit 91b9f68
Showing 1 changed file with 8 additions and 47 deletions.
55 changes: 8 additions & 47 deletions pep-0667.rst
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ They serve only to illustrate the proposed design.
return FrameLocalsProxy(self)

class FrameLocalsProxy:
"Implements collections.MutableMapping."

__slots__ "_frame"

Expand Down Expand Up @@ -332,24 +333,11 @@ They serve only to illustrate the proposed design.
continue
yield name

def pop(self):
def __contains__(self, item):
f = self._frame
co = f.f_code
if f._extra_locals:
return f._extra_locals.pop()
for index, _ in enumerate(co._variable_names):
val = f._locals[index]
if val is NULL:
continue
if index in co._cells:
cell = val
val = cell.cell_contents
if val is NULL:
continue
cell.cell_contents = NULL
else:
f._locals[index] = NULL
return val
if item in f._extra_locals:
return True
return item in co._variable_names

def __len__(self):
f = self._frame
Expand Down Expand Up @@ -387,41 +375,14 @@ This PEP and PEP 558 [2]_ share a common goal:
to make the semantics of ``locals()`` and ``frame.f_locals()``
intelligible, and their operation reliable.

In the author's opinion, PEP 558 fails to do that as it is too
complex, and has many corner cases which will lead to bugs.

The key difference between this PEP and PEP 558 is that
PEP 558 requires an internal copy of the local variables,
PEP 558 keeps an internal copy of the local variables,
whereas this PEP does not.
Maintaining a copy adds considerably to the complexity of both
the specification and implementation, and brings no real benefits.

The semantics of ``frame.f_locals``
-----------------------------------

In this PEP, ``frame.f_locals`` is a view onto the underlying frame.
It is always synchronized with the underlying frame.
In PEP 558, there is an additional copy of the local variables present
in the frame which is updated whenever ``frame.f_locals`` is accessed.
PEP 558 does not make it clear whether calls to ``locals()``
update ``frame.f_locals`` or not.

For example consider::

def foo():
x = sys._getframe().f_locals
y = locals()
print(tuple(x))
print(tuple(y))

It is not clear from PEP 558 (at time of writing) what would be printed.
Does the call to ``locals()`` update ``x``?
Would ``"y"`` be present in either ``x`` or ``y``?

With this PEP it should be clear that the above would print::
PEP 558 does not specify exactly when the internal copy is
updated, making the behavior of PEP 558 impossible to reason about.

('x', 'y')
('x',)

Open Issues
===========
Expand Down

0 comments on commit 91b9f68

Please sign in to comment.