diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index a399d1caf..73a15d1ef 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -715,8 +715,6 @@ class TestFrozenSet(TestJointOps, unittest.TestCase): s.__init__(self.otherword) self.assertEqual(s, set(self.word)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_singleton_empty_frozenset(self): f = frozenset() efs = [frozenset(), frozenset([]), frozenset(()), frozenset(''), diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index f322c1f14..8d26c5e1a 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -5,7 +5,6 @@ use super::pytype::PyTypeRef; use crate::common::hash::PyHash; use crate::common::rc::PyRc; use crate::dictdatatype; -use crate::function::OptionalArg::{Missing, Present}; use crate::function::{Args, OptionalArg}; use crate::slots::{Comparable, Hashable, Iterable, PyComparisonOp, PyIter, Unhashable}; use crate::vm::{ReprGuard, VirtualMachine}; @@ -554,8 +553,7 @@ macro_rules! multi_args_frozenset { #[pyimpl(flags(BASETYPE), with(Hashable, Comparable, Iterable))] impl PyFrozenSet { - // used by ssl.rs windows - #[allow(dead_code)] + // Also used by ssl.rs windows. pub(crate) fn from_iter( vm: &VirtualMachine, it: impl IntoIterator, @@ -573,23 +571,26 @@ impl PyFrozenSet { iterable: OptionalArg, vm: &VirtualMachine, ) -> PyResult> { - let iterable = if let Present(iterable) = iterable { - if cls.is(&vm.ctx.types.frozenset_type) { - match iterable.downcast_exact::(vm) { - Ok(iter) => return Ok(iter), - Err(iterable) => Present(PyIterable::try_from_object(vm, iterable)?), + let elements = if let OptionalArg::Present(iterable) = iterable { + let iterable = if cls.is(&vm.ctx.types.frozenset_type) { + match iterable.downcast_exact::(vm) { + Ok(fs) => return Ok(fs), + Err(iterable) => iterable, } } else { - Present(PyIterable::try_from_object(vm, iterable)?) - } + iterable + }; + vm.extract_elements(&iterable)? } else { - Missing + vec![] }; - Self { - inner: PySetInner::from_arg(iterable, vm)?, + // Return empty fs if iterable passed is emtpy and only for exact fs types. + if elements.is_empty() && cls.is(&vm.ctx.types.frozenset_type) { + Ok(vm.ctx.empty_frozenset.clone()) + } else { + Self::from_iter(vm, elements).and_then(|o| o.into_ref_with_type(vm, cls)) } - .into_ref_with_type(vm, cls) } #[pymethod(name = "__len__")] diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index 2c1d33ee2..df823f1be 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -24,7 +24,7 @@ use crate::builtins::namespace::PyNamespace; use crate::builtins::object; use crate::builtins::pystr; use crate::builtins::pytype::{self, PyType, PyTypeRef}; -use crate::builtins::set; +use crate::builtins::set::{self, PyFrozenSet}; use crate::builtins::singletons::{PyNone, PyNoneRef, PyNotImplemented, PyNotImplementedRef}; use crate::builtins::slice::PyEllipsis; use crate::builtins::staticmethod::PyStaticMethod; @@ -89,6 +89,7 @@ pub struct PyContext { pub false_value: PyIntRef, pub none: PyNoneRef, pub empty_tuple: PyTupleRef, + pub empty_frozenset: PyRef, pub ellipsis: PyRef, pub not_implemented: PyNotImplementedRef, @@ -129,6 +130,8 @@ impl PyContext { PyTuple::_new(Vec::new().into_boxed_slice()), &types.tuple_type, ); + let empty_frozenset = + PyRef::new_ref(PyFrozenSet::default(), types.frozenset_type.clone(), None); let string_cache = Dict::default(); @@ -144,6 +147,7 @@ impl PyContext { false_value, none, empty_tuple, + empty_frozenset, ellipsis, not_implemented,