Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Doc/reference/datamodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1967,8 +1967,7 @@ current call is identified based on the first argument passed to the method.
as a ``__classcell__`` entry in the class namespace. If present, this must
be propagated up to the ``type.__new__`` call in order for the class to be
initialised correctly.
Failing to do so will result in a :exc:`DeprecationWarning` in Python 3.6,
and a :exc:`RuntimeWarning` in the future.
Failing to do so will result in a :exc:`RuntimeWarning` in Python 3.8.

When using the default metaclass :class:`type`, or any metaclass that ultimately
calls ``type.__new__``, the following additional customisation steps are
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ Changes in Python behavior
in the leftmost :keyword:`for` clause).
(Contributed by Serhiy Storchaka in :issue:`10544`.)

* A :exc:`RuntimeWarning` is now emitted when the custom metaclass doesn't
provide the ``__classcell__`` entry in the namespace passed to
``type.__new__``. A :exc:`DeprecationWarning` was emitted in Python
3.6--3.7. (Contributed by Serhiy Storchaka in :issue:`23722`.)


Changes in the Python API
-------------------------
Expand Down
15 changes: 7 additions & 8 deletions Lib/test/test_super.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def __new__(cls, name, bases, namespace):

# This case shouldn't trigger the __classcell__ deprecation warning
with check_warnings() as w:
warnings.simplefilter("always", DeprecationWarning)
warnings.simplefilter("always", Warning)
class A(metaclass=Meta):
@staticmethod
def f():
Expand Down Expand Up @@ -246,22 +246,21 @@ def __new__(cls, name, bases, namespace):

# The default case should continue to work without any warnings
with check_warnings() as w:
warnings.simplefilter("always", DeprecationWarning)
warnings.simplefilter("always", Warning)
class WithoutClassRef(metaclass=Meta):
pass
self.assertEqual(w.warnings, [])

# With zero-arg super() or an explicit __class__ reference, we expect
# __build_class__ to emit a DeprecationWarning complaining that
# __build_class__ to emit a RuntimeWarning complaining that
# __class__ was not set, and asking if __classcell__ was propagated
# to type.__new__.
# In Python 3.7, that warning will become a RuntimeError.
expected_warning = (
'__class__ not set.*__classcell__ propagated',
DeprecationWarning
RuntimeWarning
)
with check_warnings(expected_warning):
warnings.simplefilter("always", DeprecationWarning)
warnings.simplefilter("always", Warning)
class WithClassRef(metaclass=Meta):
def f(self):
return __class__
Expand All @@ -270,8 +269,8 @@ def f(self):

# Check the warning is turned into an error as expected
with warnings.catch_warnings():
warnings.simplefilter("error", DeprecationWarning)
with self.assertRaises(DeprecationWarning):
warnings.simplefilter("error", RuntimeWarning)
with self.assertRaises(RuntimeWarning):
class WithClassRef(metaclass=Meta):
def f(self):
return __class__
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
A :exc:`RuntimeWarning` is now raised when the custom metaclass doesn't
provide the ``__classcell__`` entry in the namespace passed to
``type.__new__``. A :exc:`DeprecationWarning` was emitted in Python
3.6--3.7.
5 changes: 1 addition & 4 deletions Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,13 @@ builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
if (cls != NULL && PyType_Check(cls) && PyCell_Check(cell)) {
PyObject *cell_cls = PyCell_GET(cell);
if (cell_cls != cls) {
/* TODO: In 3.7, DeprecationWarning will become RuntimeError.
* At that point, cell_error won't be needed.
*/
int cell_error;
if (cell_cls == NULL) {
const char *msg =
"__class__ not set defining %.200R as %.200R. "
"Was __classcell__ propagated to type.__new__?";
cell_error = PyErr_WarnFormat(
PyExc_DeprecationWarning, 1, msg, name, cls);
PyExc_RuntimeWarning, 1, msg, name, cls);
} else {
const char *msg =
"__class__ set to %.200R defining %.200R as %.200R";
Expand Down