Skip to content

Commit

Permalink
Update to pass test for unhashable collections (RustPython#4640)
Browse files Browse the repository at this point in the history
  • Loading branch information
blue-pandaa authored Mar 7, 2023
1 parent 10eb20e commit 223fe14
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 78 deletions.
2 changes: 0 additions & 2 deletions Lib/test/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,8 +892,6 @@ def __await__(self):
self.assertFalse(isinstance(CoroLike(), Coroutine))
self.assertFalse(issubclass(CoroLike, Coroutine))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_Hashable(self):
# Check some non-hashables
non_samples = [bytearray(), list(), set(), dict()]
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 @@ -3516,8 +3516,6 @@ def test_top_level_class_var(self):

class CollectionsAbcTests(BaseTestCase):

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_hashable(self):
self.assertIsInstance(42, typing.Hashable)
self.assertNotIsInstance([], typing.Hashable)
Expand Down
10 changes: 10 additions & 0 deletions derive-impl/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ fn generate_class_def(
module_name: Option<&str>,
base: Option<String>,
metaclass: Option<String>,
unhashable: bool,
attrs: &[Attribute],
) -> Result<TokenStream> {
let doc = attrs.doc().or_else(|| {
Expand All @@ -242,6 +243,11 @@ fn generate_class_def(
Some(v) => quote!(Some(#v) ),
None => quote!(None),
};
let unhashable = if unhashable {
quote!(true)
} else {
quote!(false)
};
let basicsize = quote!(std::mem::size_of::<#ident>());
let is_pystruct = attrs.iter().any(|attr| {
attr.path.is_ident("derive")
Expand Down Expand Up @@ -290,6 +296,7 @@ fn generate_class_def(
const TP_NAME: &'static str = #module_class_name;
const DOC: Option<&'static str> = #doc;
const BASICSIZE: usize = #basicsize;
const UNHASHABLE: bool = #unhashable;
}

impl ::rustpython_vm::class::StaticType for #ident {
Expand Down Expand Up @@ -319,12 +326,15 @@ pub(crate) fn impl_pyclass(attr: AttributeArgs, item: Item) -> Result<TokenStrea
let module_name = class_meta.module()?;
let base = class_meta.base()?;
let metaclass = class_meta.metaclass()?;
let unhashable = class_meta.unhashable()?;

let class_def = generate_class_def(
ident,
&class_name,
module_name.as_deref(),
base,
metaclass,
unhashable,
attrs,
)?;

Expand Down
7 changes: 6 additions & 1 deletion derive-impl/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ impl ItemMeta for AttrItemMeta {
pub(crate) struct ClassItemMeta(ItemMetaInner);

impl ItemMeta for ClassItemMeta {
const ALLOWED_NAMES: &'static [&'static str] = &["module", "name", "base", "metaclass"];
const ALLOWED_NAMES: &'static [&'static str] =
&["module", "name", "base", "metaclass", "unhashable"];

fn from_inner(inner: ItemMetaInner) -> Self {
Self(inner)
Expand Down Expand Up @@ -299,6 +300,10 @@ impl ClassItemMeta {
self.inner()._optional_str("base")
}

pub fn unhashable(&self) -> Result<bool> {
self.inner()._bool("unhashable")
}

pub fn metaclass(&self) -> Result<Option<String>> {
self.inner()._optional_str("metaclass")
}
Expand Down
10 changes: 3 additions & 7 deletions vm/src/builtins/bytearray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ use crate::{
},
sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
types::{
AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable,
Initializer, IterNext, IterNextIterable, Iterable, PyComparisonOp, Unconstructible,
Unhashable,
AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer,
IterNext, IterNextIterable, Iterable, PyComparisonOp, Unconstructible,
},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
VirtualMachine,
Expand All @@ -41,7 +40,7 @@ use bstr::ByteSlice;
use once_cell::sync::Lazy;
use std::mem::size_of;

#[pyclass(module = false, name = "bytearray")]
#[pyclass(module = false, name = "bytearray", unhashable = true)]
#[derive(Debug, Default)]
pub struct PyByteArray {
inner: PyRwLock<PyBytesInner>,
Expand Down Expand Up @@ -100,7 +99,6 @@ pub(crate) fn init(context: &Context) {
with(
Constructor,
Initializer,
Hashable,
Comparable,
AsBuffer,
AsMapping,
Expand Down Expand Up @@ -873,8 +871,6 @@ impl AsNumber for PyByteArray {
}
}

impl Unhashable for PyByteArray {}

impl Iterable for PyByteArray {
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
Ok(PyByteArrayIterator {
Expand Down
9 changes: 3 additions & 6 deletions vm/src/builtins/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use crate::{
protocol::{PyIterIter, PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods},
recursion::ReprGuard,
types::{
AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable, Initializer,
IterNext, IterNextIterable, Iterable, PyComparisonOp, Unconstructible, Unhashable,
AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer, IterNext,
IterNextIterable, Iterable, PyComparisonOp, Unconstructible,
},
vm::VirtualMachine,
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
Expand All @@ -32,7 +32,7 @@ use std::fmt;

pub type DictContentType = dictdatatype::Dict;

#[pyclass(module = false, name = "dict")]
#[pyclass(module = false, name = "dict", unhashable = true)]
#[derive(Default)]
pub struct PyDict {
entries: DictContentType,
Expand Down Expand Up @@ -206,7 +206,6 @@ impl PyDict {
Constructor,
Initializer,
AsMapping,
Hashable,
Comparable,
Iterable,
AsSequence,
Expand Down Expand Up @@ -512,8 +511,6 @@ impl Comparable for PyDict {
}
}

impl Unhashable for PyDict {}

impl Iterable for PyDict {
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
Ok(PyDictKeyIterator::new(zelf).into_pyobject(vm))
Expand Down
18 changes: 4 additions & 14 deletions vm/src/builtins/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ use crate::{
sequence::{MutObjectSequenceOp, OptionalRangeArgs, SequenceExt, SequenceMutExt},
sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp},
types::{
AsMapping, AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext,
IterNextIterable, Iterable, PyComparisonOp, Unconstructible, Unhashable,
AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable,
Iterable, PyComparisonOp, Unconstructible,
},
utils::collection_repr,
vm::VirtualMachine,
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
};
use std::{fmt, ops::DerefMut};

#[pyclass(module = false, name = "list")]
#[pyclass(module = false, name = "list", unhashable = true)]
#[derive(Default)]
pub struct PyList {
elements: PyRwLock<Vec<PyObjectRef>>,
Expand Down Expand Up @@ -86,15 +86,7 @@ pub(crate) struct SortOptions {
pub type PyListRef = PyRef<PyList>;

#[pyclass(
with(
Constructor,
Initializer,
AsMapping,
Iterable,
Hashable,
Comparable,
AsSequence
),
with(Constructor, Initializer, AsMapping, Iterable, Comparable, AsSequence),
flags(BASETYPE)
)]
impl PyList {
Expand Down Expand Up @@ -492,8 +484,6 @@ impl Comparable for PyList {
}
}

impl Unhashable for PyList {}

fn do_sort(
vm: &VirtualMachine,
values: &mut Vec<PyObjectRef>,
Expand Down
18 changes: 4 additions & 14 deletions vm/src/builtins/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
types::AsNumber,
types::{
AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, IterNextIterable,
Iterable, PyComparisonOp, Unconstructible, Unhashable,
Iterable, PyComparisonOp, Unconstructible,
},
utils::collection_repr,
vm::VirtualMachine,
Expand All @@ -28,7 +28,7 @@ use std::{fmt, ops::Deref};

pub type SetContentType = dictdatatype::Dict<()>;

#[pyclass(module = false, name = "set")]
#[pyclass(module = false, name = "set", unhashable = true)]
#[derive(Default)]
pub struct PySet {
pub(super) inner: PySetInner,
Expand Down Expand Up @@ -70,7 +70,7 @@ impl PySet {
}
}

#[pyclass(module = false, name = "frozenset")]
#[pyclass(module = false, name = "frozenset", unhashable = true)]
#[derive(Default)]
pub struct PyFrozenSet {
inner: PySetInner,
Expand Down Expand Up @@ -489,15 +489,7 @@ fn reduce_set(
}

#[pyclass(
with(
Constructor,
Initializer,
AsSequence,
Hashable,
Comparable,
Iterable,
AsNumber
),
with(Constructor, Initializer, AsSequence, Comparable, Iterable, AsNumber),
flags(BASETYPE)
)]
impl PySet {
Expand Down Expand Up @@ -805,8 +797,6 @@ impl Comparable for PySet {
}
}

impl Unhashable for PySet {}

impl Iterable for PySet {
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
Ok(zelf.inner.iter().into_pyobject(vm))
Expand Down
8 changes: 3 additions & 5 deletions vm/src/builtins/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use crate::{
convert::ToPyObject,
function::{FuncArgs, OptionalArg, PyComparisonValue},
sliceable::SaturatedSlice,
types::{Comparable, Constructor, Hashable, PyComparisonOp, Unhashable},
types::{Comparable, Constructor, PyComparisonOp},
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
};
use num_bigint::{BigInt, ToBigInt};
use num_traits::{One, Signed, Zero};

#[pyclass(module = false, name = "slice")]
#[pyclass(module = false, name = "slice", unhashable = true)]
#[derive(Debug)]
pub struct PySlice {
pub start: Option<PyObjectRef>,
Expand All @@ -25,7 +25,7 @@ impl PyPayload for PySlice {
}
}

#[pyclass(with(Hashable, Comparable))]
#[pyclass(with(Comparable))]
impl PySlice {
#[pygetset]
fn start(&self, vm: &VirtualMachine) -> PyObjectRef {
Expand Down Expand Up @@ -258,8 +258,6 @@ impl Comparable for PySlice {
}
}

impl Unhashable for PySlice {}

#[pyclass(module = false, name = "EllipsisType")]
#[derive(Debug)]
pub struct PyEllipsis;
Expand Down
5 changes: 2 additions & 3 deletions vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1019,11 +1019,10 @@ impl SetAttr for PyType {
}
let assign = value.is_assign();

let mut attributes = zelf.attributes.write();
if let PySetterValue::Assign(value) = value {
attributes.insert(attr_name, value);
zelf.attributes.write().insert(attr_name, value);
} else {
let prev_value = attributes.remove(attr_name);
let prev_value = zelf.attributes.write().remove(attr_name);
if prev_value.is_none() {
return Err(vm.new_exception(
vm.ctx.exceptions.attribute_error.to_owned(),
Expand Down
13 changes: 12 additions & 1 deletion vm/src/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
builtins::{PyBaseObject, PyBoundMethod, PyType, PyTypeRef},
identifier,
object::{Py, PyObjectPayload, PyObjectRef, PyRef},
types::{PyTypeFlags, PyTypeSlots},
types::{hash_not_implemented, PyTypeFlags, PyTypeSlots},
vm::Context,
};
use rustpython_common::{lock::PyRwLock, static_cell};
Expand Down Expand Up @@ -60,6 +60,7 @@ pub trait PyClassDef {
const TP_NAME: &'static str;
const DOC: Option<&'static str> = None;
const BASICSIZE: usize;
const UNHASHABLE: bool = false;
}

impl<T> PyClassDef for PyRef<T>
Expand All @@ -71,6 +72,7 @@ where
const TP_NAME: &'static str = T::TP_NAME;
const DOC: Option<&'static str> = T::DOC;
const BASICSIZE: usize = T::BASICSIZE;
const UNHASHABLE: bool = T::UNHASHABLE;
}

pub trait PyClassImpl: PyClassDef {
Expand Down Expand Up @@ -112,6 +114,10 @@ pub trait PyClassImpl: PyClassDef {
.into();
class.set_attr(identifier!(ctx, __new__), bound);
}

if class.slots.hash.load().map_or(0, |h| h as usize) == hash_not_implemented as usize {
class.set_attr(ctx.names.__hash__, ctx.none.clone().into());
}
}

fn make_class(ctx: &Context) -> PyTypeRef
Expand Down Expand Up @@ -140,6 +146,11 @@ pub trait PyClassImpl: PyClassDef {
doc: Self::DOC,
..Default::default()
};

if Self::UNHASHABLE {
slots.hash.store(Some(hash_not_implemented));
}

Self::extend_slots(&mut slots);
slots
}
Expand Down
10 changes: 4 additions & 6 deletions vm/src/stdlib/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ mod _collections {
sequence::{MutObjectSequenceOp, OptionalRangeArgs},
sliceable::SequenceIndexOp,
types::{
AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, IterNextIterable,
Iterable, PyComparisonOp, Unhashable,
AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable, Iterable,
PyComparisonOp,
},
utils::collection_repr,
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
Expand All @@ -27,7 +27,7 @@ mod _collections {
use std::collections::VecDeque;

#[pyattr]
#[pyclass(name = "deque")]
#[pyclass(name = "deque", unhashable = true)]
#[derive(Debug, Default, PyPayload)]
struct PyDeque {
deque: PyRwLock<VecDeque<PyObjectRef>>,
Expand Down Expand Up @@ -57,7 +57,7 @@ mod _collections {

#[pyclass(
flags(BASETYPE),
with(Constructor, Initializer, AsSequence, Comparable, Hashable, Iterable)
with(Constructor, Initializer, AsSequence, Comparable, Iterable)
)]
impl PyDeque {
#[pymethod]
Expand Down Expand Up @@ -574,8 +574,6 @@ mod _collections {
}
}

impl Unhashable for PyDeque {}

impl Iterable for PyDeque {
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
Ok(PyDequeIterator::new(zelf).into_pyobject(vm))
Expand Down
Loading

0 comments on commit 223fe14

Please sign in to comment.