strange python behaviour with mixing globals/parameters and function named 'top'
Asked Answered
E

1

21

The following code (not directly in an interpreter, but execute as file)

def top(deck):
    pass

def b():
    global deck

produces the error

SyntaxError: name 'deck' is local and global

on python2.6.4 and

SyntaxError: name 'deck' is parameter and global

on python 3.1

python2.4 seems to accept this code, so does the 2.6.4 interactive interpreter.

This is already odd; why is 'deck' conflicting if it's a global in one method and a parameter in the other?

But it gets weirder. Rename 'top' to basically anything else, and the problem disappears.

Can someone explain this behaviour? I feel like I'm missing something very obvious here. Is the name 'top' somehow affecting certain scoping internals?

Update

This indeed appears to be a bug in the python core. I have filed a bug report.

Evasive answered 30/9, 2010 at 7:23 Comment(6)
FWIW, I can confirm this behavior in 2.6.5 and 3.1.2.Mistiemistime
Adding print top yields "name 'top' is not defined", so at least it's not a function or something. Odd.David
@Aaron indeed, tried that as well.Evasive
Exchanging the two methods also fixes the problem (b first, then top).David
From the source code (svn.python.org/projects/python/trunk/Python/symtable.c), it seems that the parser somehow thinks the body of b is part of the function body of top (i.e. that the parameter and the global are in the same scope).David
Can anyone find another name besides top that triggers the bug? For the record, id and hash of top don't look odd (3074663844=0xb74465a4, -1220303452=-0x48bb9a5c)David
O
13

It looks like it is a bug in the symbol table handling. Python/symtable.c has some code that (although somewhat obfuscated) does indeed treat 'top' as a special identifier:

if (!GET_IDENTIFIER(top) ||
    !symtable_enter_block(st, top, ModuleBlock, (void *)mod, 0)) {
    PySymtable_Free(st);
    return NULL;
}

followed somewhat later by:

if (name == GET_IDENTIFIER(top))
    st->st_global = st->st_cur->ste_symbols;

Further up the file there's a macro:

#define GET_IDENTIFIER(VAR) \
    ((VAR) ? (VAR) : ((VAR) = PyString_InternFromString(# VAR)))

which uses the C preprocessor to initialise the variable top to an interned string with the name of the variable.

I think the symbol table must be using the name 'top' to refer to the top level code, but why it doesn't use something that can't conflict with a real variable I have no idea.

I would report it as a bug if I were you.

Oleviaolfaction answered 30/9, 2010 at 8:12 Comment(5)
-1 Can't follow you. The runtime assembler code can't see that the variable was called "top" in the source. This would be different if there was a string "top" anywhere.David
The macro effectively boils down to: ((top)?(top):((top)=PyString_InternFromString("top"))) (the C pre-processor syntax # VAR means turn the value of the macro parameter into a string)Oleviaolfaction
So, if I understand correctly, it looks as if "top" gets defined by accident as the global name space identifier. If the variable in C code was named _cookies, the conflict would be with a function named _cookies.Evasive
The name of the variable in the C code doesn't really matter, that's just how the macro works. It could (and should!) use 'top' as the C variable but " o/~ la la teeny tiny puppies... o/~ " as the Python symbol... Or at least something that is in no way a valid Python symbol ;PPygmy
Yes, I maybe wasn't clear enough that I was referring to the value of top as being the problem rather than the name. BTW, symtable.py also has special knowledge of this name though the fundamental problem is in the C code.Oleviaolfaction

© 2022 - 2024 — McMap. All rights reserved.