Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
__slots__ xor __dict__
  • Loading branch information
youknowone committed Dec 10, 2025
commit f893da6d30f62f3b9f3b9f5758e8554e1a4e43a0
2 changes: 0 additions & 2 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -5455,8 +5455,6 @@ def test_bug_1028306(self):
self.assertEqual(as_datetime, datetime_sc)
self.assertEqual(datetime_sc, as_datetime)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_extra_attributes(self):
with self.assertWarns(DeprecationWarning):
utcnow = datetime.utcnow()
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_contextlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ def __exit__(self, *args):
manager = DefaultEnter()
self.assertIs(manager.__enter__(), manager)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_slots(self):
class DefaultContextManager(AbstractContextManager):
__slots__ = ()
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_contextlib_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ async def __aexit__(self, *args):
async with manager as context:
self.assertIs(manager, context)

# TODO: RUSTPYTHON
@unittest.expectedFailure
async def test_slots(self):
class DefaultAsyncContextManager(AbstractAsyncContextManager):
__slots__ = ()
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2776,8 +2776,6 @@ class C:


class TestSlots(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_simple(self):
@dataclass
class C:
Expand Down Expand Up @@ -2819,8 +2817,6 @@ class Derived(Base):
# We can add a new field to the derived instance.
d.z = 10

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_generated_slots(self):
@dataclass(slots=True)
class C:
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2923,10 +2923,6 @@ class CPythonAPItests(PythonAPItests, unittest.TestCase):
class PyPythonAPItests(PythonAPItests, unittest.TestCase):
decimal = P

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_complex(self): # TODO(RUSTPYTHON): Remove this test when it pass
return super().test_complex()

class ContextAPItests:

Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_fractions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1137,8 +1137,6 @@ def test_copy_deepcopy_pickle(self):
self.assertTypedEquals(dr, copy(dr))
self.assertTypedEquals(dr, deepcopy(dr))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_slots(self):
# Issue 4998
r = F(13, 7)
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3278,7 +3278,6 @@ def test_cached_attribute_name_differs_from_func_name(self):
self.assertEqual(item.get_cost(), 4)
self.assertEqual(item.cached_cost, 3)

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_object_with_slots(self):
item = CachedCostItemWithSlots()
with self.assertRaisesRegex(
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -5332,7 +5332,6 @@ class A(os.PathLike):
def test_pathlike_class_getitem(self):
self.assertIsInstance(os.PathLike[bytes], types.GenericAlias)

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_pathlike_subclass_slots(self):
class A(os.PathLike):
__slots__ = ()
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2889,8 +2889,6 @@ class TestNormalDist:
# inaccurate. There isn't much we can do about this short of
# implementing our own implementations from scratch.

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_slots(self):
nd = self.module.NormalDist(300, 23)
with self.assertRaises(TypeError):
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5269,7 +5269,6 @@ def test_weakref_all(self):
for t in things:
self.assertEqual(weakref.ref(t)(), t)

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

@unittest.expectedFailure # TODO: RUSTPYTHON - __slots__ with Generic doesn't prevent new attributes
def test_parameterized_slots_dict(self):
T = TypeVar('T')
class D(Generic[T]):
Expand Down
12 changes: 9 additions & 3 deletions crates/vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,16 @@ impl Constructor for PyBaseObject {
}

// more or less __new__ operator
let dict = if cls.is(vm.ctx.types.object_type) {
None
} else {
// Only create dict if the class has HAS_DICT flag (i.e., __slots__ was not defined
// or __dict__ is in __slots__)
let dict = if cls
.slots
.flags
.has_feature(crate::types::PyTypeFlags::HAS_DICT)
{
Some(vm.ctx.new_dict())
} else {
None
};

// Ensure that all abstract methods are implemented before instantiating instance.
Expand Down
12 changes: 10 additions & 2 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,12 @@ impl PyType {
) -> Result<PyRef<Self>, String> {
let mro = Self::resolve_mro(&bases)?;

if base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
// Inherit HAS_DICT from any base in MRO that has it
// (not just the first base, as any base with __dict__ means subclass needs it too)
if mro
.iter()
.any(|b| b.slots.flags.has_feature(PyTypeFlags::HAS_DICT))
{
slots.flags |= PyTypeFlags::HAS_DICT
}

Expand Down Expand Up @@ -1223,7 +1228,10 @@ impl Constructor for PyType {
// since they inherit it from type

// Add __dict__ descriptor after type creation to ensure correct __objclass__
if !base_is_type {
// Only add if:
// 1. base is not type (type subclasses inherit __dict__ from type)
// 2. the class has HAS_DICT flag (i.e., __slots__ was not defined or __dict__ is in __slots__)
if !base_is_type && typ.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
let __dict__ = identifier!(vm, __dict__);
if !typ.attributes.read().contains_key(&__dict__) {
unsafe {
Expand Down
7 changes: 6 additions & 1 deletion crates/vm/src/stdlib/typevar.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// spell-checker:ignore typevarobject funcobj
use crate::{
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
AsObject, Context, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
builtins::{PyTupleRef, PyTypeRef, pystr::AsPyStr},
common::lock::PyMutex,
function::{FuncArgs, IntoFuncArgs, PyComparisonValue},
Expand Down Expand Up @@ -972,6 +972,11 @@ pub struct Generic {}

#[pyclass(flags(BASETYPE))]
impl Generic {
#[pyattr]
fn __slots__(ctx: &Context) -> PyTupleRef {
ctx.empty_tuple.clone()
}

#[pyclassmethod]
fn __class_getitem__(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
call_typing_args_kwargs("_generic_class_getitem", cls, args, vm)
Expand Down