You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Missing nested scope vars in class scope (bis) #64178
bpo-9226: erroneous behavior when creating classes inside a closure
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=<Date2018-07-25.18:38:41.694>created_at=<Date2013-12-13.21:31:17.377>labels= ['interpreter-core', 'type-feature']
title='Missing nested scope vars in class scope (bis)'updated_at=<Date2018-07-25.18:38:41.691>user='https://github.com/arigo'
This is a repeat of the old bpo-532860: "NameError assigning to class in a func". It is about class statements' variable lookups, which has different behavior at module level or in a nested scope:
deff(n):
classA:
n=n# doesn't work, tries to look up 'n' as a global
The point of repeating this very old issue is the much more recent bpo-17853: "Conflict between lexical scoping and name injection in __prepare__". This was a slightly different problem, but resolved by adding the exact opcode that would be needed to fix the old issue too: LOAD_CLASSDEREF. It's an opcode which looks in the locals dict for a name, and if not found, falls back to freevars instead of to globals.
The present bug report is here to argue that this new opcode should be used a bit more systematically by the compiler. Contrary to the conclusions reached in the very old bug report, I argue that nowadays it would seem reasonable to expect that the previous example should work. By no means is it an essential issue in my opinion, but this would probably be a slight simplification and prevent corner-case surprizes. Moreover it probably leads to a simplification in the compiler: no need to track which variables are local or not in class bodies --- instead just compile the loading of 'n' above as a LOAD_CLASSDEREF without worrying about the fact that the same 'n' is also assigned to in the same scope.
As near as I can tell, "class A: n = n" currently works the same at module and nested scope, the latter with or without nonlocal n.
>>> classA: n=n
[...]
NameError: name 'n' is not defined
>>> deff():
class A: n=n
>>> f()
[...]
NameError: name 'n' is not defined
>>> deff(n):
class A: n=n
>>> f(2)
[...]
NameError: name 'n' is not defined
Repeat after 'n=1' at module scope and the NameErrors disappear. It appears that you are asking that the class statement be made to act differently when nested instead of the same. This would break code that depends on the current behavior. This would need discussion on python-ideas and pydev lists.
bpo-17853 was in the context of metaclasses. Even so, I am puzzled by the opening statement there that
fromenumimportEnum# I added this as necessaryclassSeason(Enum):
SPRING=Season()
"works beautifully" at top level as it indeed raises
NameError: name 'Season' is not defined
My point here is that changing the output of
n=1deff(n=2):
classA: n=nreturnAprint(f().n)
from 1, as it has been from the beginning (except for the print change) to 2, which I believe is implied in the request, is not the sort of syntax change we do. Hence I think this should be closed.
from enum import Enum # I added this as necessary
class Season(Enum):
SPRING = Season()
"works beautifully" at top level as it indeed raises
NameError: name 'Season' is not defined
Pay close attention to the line just before that example:
I tried having the metaclass insert an object into the custom dict
(aka namespace) returned by __prepare__; this object has the same
name as the to-be-created class.
It does not raise a NameError because the name was injected via the metaclass __prepare__ method. (Or did at that time -- I don't think that bit of cleverness made the final cut.)
As far as this enhancement request goes, I think the compatibility break is likely too large to have it in the 3.x series.
arigo commented on May 19, 2014
arigomannequin
MannequinAuthor
More actions
Terry: I meant exactly what I wrote, and not some unrelated examples:
deff():
n=1classA: n=n
doesn't work, but the same two lines ("n = 1"; "class A: n = n") work if written at module level instead of in a function.
>>> deff():
... n =1
... classC:
... n = n
... print(n)
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f
File "<stdin>", line 4, in C
NameError: name 'n' is not defined
The issue is that the latter will still emit a LOAD_NAME/STORE_NAME pair, while the former emits LOAD_CLASSDEREF (and has emitted LOAD_DEREF for as long as I can recall hacking on the compiler).
Off the top of my head, I'm not sure our current symbol table analysis pass can actually cope with this idea though - it would require separating "just a class body local, which may or may not first be retrieved as a global or builtin" from "a class body local which is first retrieved from a closure reference".
Activity
arigo commentedon Dec 13, 2013
This is a repeat of the old bpo-532860: "NameError assigning to class in a func". It is about class statements' variable lookups, which has different behavior at module level or in a nested scope:
The point of repeating this very old issue is the much more recent bpo-17853: "Conflict between lexical scoping and name injection in __prepare__". This was a slightly different problem, but resolved by adding the exact opcode that would be needed to fix the old issue too: LOAD_CLASSDEREF. It's an opcode which looks in the locals dict for a name, and if not found, falls back to freevars instead of to globals.
The present bug report is here to argue that this new opcode should be used a bit more systematically by the compiler. Contrary to the conclusions reached in the very old bug report, I argue that nowadays it would seem reasonable to expect that the previous example should work. By no means is it an essential issue in my opinion, but this would probably be a slight simplification and prevent corner-case surprizes. Moreover it probably leads to a simplification in the compiler: no need to track which variables are local or not in class bodies --- instead just compile the loading of 'n' above as a LOAD_CLASSDEREF without worrying about the fact that the same 'n' is also assigned to in the same scope.
terryjreedy commentedon Dec 23, 2013
As near as I can tell, "class A: n = n" currently works the same at module and nested scope, the latter with or without nonlocal n.
Repeat after 'n=1' at module scope and the NameErrors disappear. It appears that you are asking that the class statement be made to act differently when nested instead of the same. This would break code that depends on the current behavior. This would need discussion on python-ideas and pydev lists.
terryjreedy commentedon May 19, 2014
bpo-17853 was in the context of metaclasses. Even so, I am puzzled by the opening statement there that
"works beautifully" at top level as it indeed raises
NameError: name 'Season' is not defined
My point here is that changing the output of
from 1, as it has been from the beginning (except for the print change) to 2, which I believe is implied in the request, is not the sort of syntax change we do. Hence I think this should be closed.
ethanfurman commentedon May 19, 2014
Terry remarked:
---------------
Pay close attention to the line just before that example:
It does not raise a NameError because the name was injected via the metaclass __prepare__ method. (Or did at that time -- I don't think that bit of cleverness made the final cut.)
As far as this enhancement request goes, I think the compatibility break is likely too large to have it in the 3.x series.
arigo commentedon May 19, 2014
Terry: I meant exactly what I wrote, and not some unrelated examples:
doesn't work, but the same two lines ("n = 1"; "class A: n = n") work if written at module level instead of in a function.
ncoghlan commentedon Jun 20, 2015
With the fact that the existence of "Python without closures" predates Python 2.2, this now reads like a straight up compiler bug to me.
Compare the behaviour with no local assignment in the class body:
To the behaviour once the local is assigned:
The issue is that the latter will still emit a LOAD_NAME/STORE_NAME pair, while the former emits LOAD_CLASSDEREF (and has emitted LOAD_DEREF for as long as I can recall hacking on the compiler).
Off the top of my head, I'm not sure our current symbol table analysis pass can actually cope with this idea though - it would require separating "just a class body local, which may or may not first be retrieved as a global or builtin" from "a class body local which is first retrieved from a closure reference".
serhiy-storchaka commentedon Mar 22, 2018
This looks like a duplicate of bpo-9226.
taleinat commentedon Jul 25, 2018
This does indeed seem to be a duplicate of bpo-9226.