Issue 28891: Standardise more behaviours for zero-argument super() class and classcell (original) (raw)
In http://bugs.python.org/issue28884#msg282535 Armin Rigo points out that the zero-argument super() cell injection currently interacts a little strangely with explicit "nonlocal class" statements in nested namespaces.
Specifically, it acts like a closure variable that isn't visible in its own scope, but can be accessed via nonlocal in nested class scopes:
class C_with_nested_nonlocal: ... class Inner: ... nonlocal class ... class = 42 ... print(locals()) ... {'module': 'main', 'qualname': 'C_with_nested_nonlocal', 'Inner': <class '__main__.C_with_nested_nonlocal.Inner'>, 'class': 42}
This weird behaviour is due to the way the CPython code generator injects class into the namespaces we track during symbol table generation (specifically, it's made available as a free variable for nested namespaces to reference without actually adding it to the local symbol namespace for the class body). There's no requirement for other implementations to replicate the full details of that idiosyncratic behaviour, but there is a requirement that class and (in 3.6+) classcell not show up in locals() by default when evaluating the class body.
And methods can similarly overwrite the interpreter provided reference to the defining class:
class C_with_method_assignment_to_class_cell: ... def bad_method(self): ... nonlocal class ... class = 42 ... def other_method(self): ... return class ... bad_method(None) ... print(locals()["class"]) ... 42 C_with_method_assignment_to_class_cell().other_method() <class '__main__.C_with_method_assignment_to_class_cell'> C_with_method_assignment_to_class_cell().bad_method() C_with_method_assignment_to_class_cell().other_method() 42
One possible approach here would be to implement an outright language level ban on the use of "class" in explicit "nonlocal" declarations, and then add a test to Lib/test_super.py that ensures "class" and "classcell" don't show up in the class locals() while the statement body is executing.