Skip to content

Commit d8cdc72

Browse files
committed
__slots__ xor __dict__
1 parent 98fff96 commit d8cdc72

File tree

4 files changed

+19
-7
lines changed

4 files changed

+19
-7
lines changed

Lib/test/test_typing.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5269,7 +5269,6 @@ def test_weakref_all(self):
52695269
for t in things:
52705270
self.assertEqual(weakref.ref(t)(), t)
52715271

5272-
@unittest.expectedFailure # TODO: RUSTPYTHON - __slots__ with Generic doesn't prevent new attributes
52735272
def test_parameterized_slots(self):
52745273
T = TypeVar('T')
52755274
class C(Generic[T]):
@@ -5289,7 +5288,6 @@ def foo(x: C['C']): ...
52895288
self.assertEqual(get_type_hints(foo, globals(), locals())['x'], C[C])
52905289
self.assertEqual(copy(C[int]), deepcopy(C[int]))
52915290

5292-
@unittest.expectedFailure # TODO: RUSTPYTHON - __slots__ with Generic doesn't prevent new attributes
52935291
def test_parameterized_slots_dict(self):
52945292
T = TypeVar('T')
52955293
class D(Generic[T]):

crates/vm/src/builtins/object.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,16 @@ impl Constructor for PyBaseObject {
6666
}
6767

6868
// more or less __new__ operator
69-
let dict = if cls.is(vm.ctx.types.object_type) {
70-
None
71-
} else {
69+
// Only create dict if the class has HAS_DICT flag (i.e., __slots__ was not defined
70+
// or __dict__ is in __slots__)
71+
let dict = if cls
72+
.slots
73+
.flags
74+
.has_feature(crate::types::PyTypeFlags::HAS_DICT)
75+
{
7276
Some(vm.ctx.new_dict())
77+
} else {
78+
None
7379
};
7480

7581
// Ensure that all abstract methods are implemented before instantiating instance.

crates/vm/src/builtins/type.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,10 @@ impl Constructor for PyType {
12231223
// since they inherit it from type
12241224

12251225
// Add __dict__ descriptor after type creation to ensure correct __objclass__
1226-
if !base_is_type {
1226+
// Only add if:
1227+
// 1. base is not type (type subclasses inherit __dict__ from type)
1228+
// 2. the class has HAS_DICT flag (i.e., __slots__ was not defined or __dict__ is in __slots__)
1229+
if !base_is_type && typ.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
12271230
let __dict__ = identifier!(vm, __dict__);
12281231
if !typ.attributes.read().contains_key(&__dict__) {
12291232
unsafe {

crates/vm/src/stdlib/typevar.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// spell-checker:ignore typevarobject funcobj
22
use crate::{
3-
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
3+
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
44
builtins::{PyTupleRef, PyTypeRef, pystr::AsPyStr},
55
common::lock::PyMutex,
66
function::{FuncArgs, IntoFuncArgs, PyComparisonValue},
@@ -972,6 +972,11 @@ pub struct Generic {}
972972

973973
#[pyclass(flags(BASETYPE))]
974974
impl Generic {
975+
#[pyattr]
976+
fn __slots__(ctx: &Context) -> PyTupleRef {
977+
ctx.empty_tuple.clone()
978+
}
979+
975980
#[pyclassmethod]
976981
fn __class_getitem__(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
977982
call_typing_args_kwargs("_generic_class_getitem", cls, args, vm)

0 commit comments

Comments
 (0)