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.

Ivo van der Wijk
  • 16,341
  • 4
  • 43
  • 57

1 Answers1

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.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • -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. – Aaron Digulla Sep 30 '10 at 08:23
  • 1
    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) – Duncan Sep 30 '10 at 08:41
  • 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. – Ivo van der Wijk Sep 30 '10 at 09:26
  • 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 ;P – Thomas Wouters Sep 30 '10 at 11:27
  • 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. – Duncan Sep 30 '10 at 12:30