Skip to content

Commit e513bf1

Browse files
authored
Fix metaclass check classcell (RustPython#3986)
1 parent 2b2ed5a commit e513bf1

File tree

4 files changed

+27
-8
lines changed

4 files changed

+27
-8
lines changed

Lib/test/test_super.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,6 @@ def f():
143143
return __class__
144144
self.assertIs(X.f(), X)
145145

146-
# TODO: RUSTPYTHON
147-
@unittest.expectedFailure
148146
def test___class___new(self):
149147
# See issue #23722
150148
# Ensure zero-arg super() works as soon as type.__new__() is completed
@@ -263,8 +261,6 @@ class WithClassRef(metaclass=Meta):
263261
def f(self):
264262
return __class__
265263

266-
# TODO: RUSTPYTHON
267-
@unittest.expectedFailure
268264
def test___classcell___overwrite(self):
269265
# See issue #23722
270266
# Overwriting __classcell__ with nonsense is explicitly prohibited
@@ -279,8 +275,6 @@ def __new__(cls, name, bases, namespace, cell):
279275
class A(metaclass=Meta, cell=bad_cell):
280276
pass
281277

282-
# TODO: RUSTPYTHON
283-
@unittest.expectedFailure
284278
def test___classcell___wrong_cell(self):
285279
# See issue #23722
286280
# Pointing the cell reference at the wrong class is also prohibited

vm/src/builtins/type.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ use crate::common::{
88
lock::{PyRwLock, PyRwLockReadGuard},
99
};
1010
use crate::{
11+
builtins::function::PyCellRef,
1112
builtins::PyBaseExceptionRef,
1213
class::{PyClassImpl, StaticType},
14+
convert::ToPyObject,
1315
function::{FuncArgs, KwArgs, OptionalArg, PySetterValue},
1416
identifier,
1517
protocol::PyNumberMethods,
1618
types::{Callable, GetAttr, PyTypeFlags, PyTypeSlots, SetAttr},
17-
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
19+
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
1820
};
1921
use indexmap::{map::Entry, IndexMap};
2022
use itertools::Itertools;
@@ -622,6 +624,16 @@ impl PyType {
622624
let typ = Self::new_verbose_ref(name.as_str(), base, bases, attributes, slots, metatype)
623625
.map_err(|e| vm.new_type_error(e))?;
624626

627+
if let Some(cell) = typ.attributes.write().get(identifier!(vm, __classcell__)) {
628+
let cell = PyCellRef::try_from_object(vm, cell.clone()).map_err(|_| {
629+
vm.new_type_error(format!(
630+
"__classcell__ must be a nonlocal cell, not {}",
631+
cell.class().name()
632+
))
633+
})?;
634+
cell.set(Some(typ.clone().to_pyobject(vm)));
635+
};
636+
625637
// avoid deadlock
626638
let attributes = typ
627639
.attributes

vm/src/stdlib/builtins.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,19 @@ mod builtins {
913913
)?;
914914

915915
if let Some(ref classcell) = classcell {
916-
classcell.set(Some(class.clone()));
916+
let classcell = classcell.get().ok_or_else(|| {
917+
vm.new_type_error(format!(
918+
"__class__ not set defining {:?} as {:?}. Was __classcell__ propagated to type.__new__?",
919+
meta_name, class
920+
))
921+
})?;
922+
923+
if !classcell.is(&class) {
924+
return Err(vm.new_type_error(format!(
925+
"__class__ set to {:?} defining {:?} as {:?}",
926+
classcell, meta_name, class
927+
)));
928+
}
917929
}
918930

919931
Ok(class)

vm/src/vm/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ declare_const_name! {
8787
__ceil__,
8888
__cformat__,
8989
__class__,
90+
__classcell__,
9091
__class_getitem__,
9192
__complex__,
9293
__contains__,

0 commit comments

Comments
 (0)