This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Standardise more behaviours for zero-argument super() __class__ and __classcell__
Type: behavior Stage: test needed
Components: Versions:
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: arigo, ncoghlan
Priority: normal Keywords:

Created on 2016-12-07 01:24 by ncoghlan, last changed 2022-04-11 14:58 by admin.

Messages (2)
msg282585 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2016-12-07 01:24
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.
msg282587 - (view) Author: Nick Coghlan (ncoghlan) * (Python committer) Date: 2016-12-07 01:36
Breaking the concrete proposal out to its own post:

- for CPython, "nonlocal __class__" becomes a SyntaxError generated by the symbol table pass in the compiler in 3.7+ and a DeprecationWarning in 3.6.1+
- other implementations (including PyPy) can just make "nonlocal __class__" a SyntaxError immediately
- an explicit test be added to https://hg.python.org/cpython/file/default/Lib/test/test_super.py for 3.6+ that "__class__" and "__classcell__" don't show up in the class locals() during execution of the class body
History
Date User Action Args
2022-04-11 14:58:40adminsetgithub: 73077
2016-12-07 09:32:35serhiy.storchakalinkissue28884 dependencies
2016-12-07 01:36:23ncoghlansetmessages: + msg282587
2016-12-07 01:24:28ncoghlancreate