8000 Allow update `__class__` for mutable types or module subclasses (#5225) · RustPython/RustPython@4d5cf24 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4d5cf24

Browse files
authored
Allow update __class__ for mutable types or module subclasses (#5225)
* Allow update `__class__` for module subclasses * Set `IMMUTABLETYPE` flag for static classes * Unmark fixed tests
1 parent 8c7b811 commit 4d5cf24

File tree

3 files changed

+18
-6
lines changed

3 files changed

+18
-6
lines changed

Lib/test/test_opcache.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,6 @@ def f():
152152
for _ in range(1025):
153153
self.assertTrue(f())
154154

155-
# TODO: RUSTPYTHON
156-
@unittest.expectedFailure
157155
def test_metaclass_swap(self):
158156
class OldMetaclass(type):
159157
@property
@@ -411,8 +409,6 @@ def f():
411409
for _ in range(1025):
412410
self.assertTrue(f())
413411

414-
# TODO: RUSTPYTHON
415-
@unittest.expectedFailure
416412
def test_metaclass_swap(self):
417413
class OldMetaclass(type):
418414
@property

derive-impl/src/pyclass.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,9 @@ fn extract_impl_attrs(attr: AttributeArgs, item: &Ident) -> Result<ExtractedImpl
14971497
.union(::rustpython_vm::types::PyTypeFlags::#ident)
14981498
});
14991499
}
1500+
flags.push(quote! {
1501+
.union(::rustpython_vm::types::PyTypeFlags::IMMUTABLETYPE)
1502+
});
15001503
} else {
15011504
bail_span!(path, "Unknown pyimpl attribute")
15021505
}

vm/src/builtins/object.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::{PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef};
22
use crate::common::hash::PyHash;
3+
use crate::types::PyTypeFlags;
34
use crate::{
45
class::PyClassImpl,
56
function::{Either, FuncArgs, PyArithmeticValue, PyComparisonValue, PySetterValue},
@@ -281,7 +282,18 @@ impl PyBaseObject {
281282

282283
#[pygetset(name = "__class__", setter)]
283284
fn set_class(instance: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
284-
if instance.payload_is::<PyBaseObject>() {
285+
let both_module = instance.class().fast_issubclass(vm.ctx.types.module_type)
286+
&& value.class().fast_issubclass(vm.ctx.types.module_type);
287+
let both_mutable = !instance
288+
.class()
289+
.slots
290+
.flags
291+
.has_feature(PyTypeFlags::IMMUTABLETYPE)
292+
&& !value
293+
.downcast_ref::<PyType>()
294+
.map(|t| t.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE))
295+
.unwrap_or(false);
296+
if both_mutable || both_module {
285297
match value.downcast::<PyType>() {
286298
Ok(cls) => {
287299
// FIXME(#1979) cls instances might have a payload
@@ -298,7 +310,8 @@ impl PyBaseObject {
298310
}
299311
} else {
300312
Err(vm.new_type_error(
301-
"__class__ assignment only supported for types without a payload".to_owned(),
313+
"__class__ assignment only supported for mutable types or ModuleType subclasses"
314+
.to_owned(),
302315
))
303316
}
304317
}

0 commit comments

Comments
 (0)
0