Conflict between docs & behavior (was Re: Another newbee question)

Tim Peters (tim@ksr.com)
Wed, 13 Apr 94 03:13:33 -0400

[those not fascinated by the subtleties of name resolution should
feel free to skip this!
]

> [tim, believing everything he reads again <wink>]
> ...
> I believed that remaining DELETE_NAMEs could be changed to
> DELETE_GLOBALs, but didn't do that ...

Well, this is interesting! I believe that when you put in compile.c's
"optimize" function, it made a subtle change to language semantics, but
that nobody has noticed. BTW, the LOAD_GLOBAL patch doesn't make it any
worse.

The question is what should this module do?

a = 12
def f():
del a
f()

It actually raises "NameError: undefined local variable" today. But the
Reference Manual, section 4.1 (Code blocks, execution frames, and name
spaces) sez that a name is local to a block if and only if it's _bound_
(anywhere) in the block, and explicitly says "a target occurring in a del
statement does not bind a name". So the docs clearly say that "a" is
global in f, while the implementation doesn't agree.

Now when I hacked in the LOAD_GLOBAL patch, the comments in optimize said
that the local-variable-finding phase did _not_ look at DELETE_NAME
instructions, but the code actually _did_ look at them. And I believe
that's the cause of the discrepancy ("a" is an argument to DELETE_NAME,
so "a" gets stuffed onto the list of locals, so the DELETE_NAME on "a"
gets optimized to DELETE_FAST, and the global "a" is inaccessible).

Note 1: The patch I posted changed the comments to match the code.

Note 2: If you want to keep it this way, I take back the quoted comment
about optimizing DELETE_NAME -> DELETE_GLOBAL above (since all
DELETE_NAMEs are optimized to DELETE_FASTs now, there's nothing
else to be done).

Opinion: Leave the implementation alone and change the reference manual
to match it (-> a name's local iff it's bound or appears as the target of
del):

+ The doc's decision about del targets predates the introduction of the
"global" statement, back to the days when there was no non-excruciating
way for a function to alter a global in any way. If a function
_really_ wants to del a global, it can declare it "global" now.

+ It's more consistent the way it is (since every other attempt to mangle
a global now requires a "global" statement to make the global mangle-
able).

+ If you change the implementation instead of the docs, then depending on
the value of "string", this variation will either delete the global "a"
or won't touch it (e.g., if string=='a=3\n'):

a = 12
def f():
exec(string)
del a
f()

The way things are now, we know for a fact, by static inspection, that
the global "a" won't get clobbered (assuming a non-perverse exec
argument). I think that's Good.

+ If you changed the implementation now, millions of lines of code would
break <snort>.

when-you've-got-a-good-point-beat-it-to-death<grin>-ly y'rs - tim

Tim Peters tim@ksr.com
not speaking for Kendall Square Research Corp