Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
39 changes: 33 additions & 6 deletions src/core/IronPython.Modules/_collections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -572,14 +572,20 @@ public deque InPlaceAdd(object other) {

#region binary operators

public static deque operator +([NotNone] deque x, object y) {
if (y is deque t) return x + t;
[SpecialName]
public static deque Add(CodeContext context, [NotNone] deque x, object y) {
if (y is deque t) return Add(context, x, t);
throw PythonOps.TypeError($"can only concatenate deque (not \"{PythonOps.GetPythonTypeName(y)}\") to deque");
}

public static deque operator +([NotNone] deque x, [NotNone] deque y) {
var d = new deque(x._maxLen);
d.extend(x);
[SpecialName]
public static deque Add(CodeContext context, [NotNone] deque x, [NotNone] deque y) {
var d = (deque)__new__(context, DynamicHelpers.GetPythonType(x), null, null);
if (x._maxLen > 0) {
d.__init__(x, x._maxLen);
} else {
d.__init__(x);
}
d.extend(y);
return d;
}
Expand Down Expand Up @@ -812,6 +818,27 @@ public int __length_hint__() {

#region private members

private object[] GetObjectArray() {
lock (_lockObj) {
if (_itemCnt == 0) return [];

object[] arr = new object[_itemCnt];
int cnt1, cnt2;
if (_head >= _tail) {
cnt1 = _data.Length - _head;
cnt2 = _itemCnt - cnt1;
} else {
cnt1 = _itemCnt;
cnt2 = 0;
}

Array.Copy(_data, _head, arr, 0, cnt1);
Array.Copy(_data, 0, arr, cnt1, cnt2);
return arr;
}
}


private void GrowArray() {
// do nothing if array is already at its max length
if (_data.Length == _maxLen) return;
Expand Down Expand Up @@ -961,7 +988,7 @@ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) {
int res;
CompareUtil.Push(this);
try {
res = ((IStructuralEquatable)new PythonTuple(this)).GetHashCode(comparer);
res = ((IStructuralEquatable)new PythonTuple(GetObjectArray())).GetHashCode(comparer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want PythonTuple.MakeTuple instead of the constructor? Not even sure when this code path gets called anyway?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want PythonTuple.MakeTuple instead of the constructor?

Yes!

Not even sure when this code path gets called anyway?

IDK either, it is not being tested by any of our tests. I have tested GetObjectArray in vitro. I guess it's there so that deque plays nicely with the rest of the .NET containers.

} finally {
CompareUtil.Pop(this);
}
Expand Down
36 changes: 36 additions & 0 deletions tests/suite/test_deque.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,40 @@ def test_maxlen_value(self):
self.assertRaises(OverflowError, deque, [], -1<<64)
self.assertRaisesMessage(ValueError, "maxlen must be non-negative", deque, [], -1)


def test_add(self):
d1 = deque([1, 2, 3], maxlen=6)
d2 = deque([4, 5, 6], maxlen=4)
d2 = deque([4, 5, 6])
d3 = d1 + d2
self.assertEqual(d3, deque([1, 2, 3, 4, 5, 6]))
self.assertIsInstance(d3, deque)
self.assertEqual(d3.maxlen, 6)

class Deque(deque): pass
sd1 = Deque([1, 2, 3])
sd2 = Deque([4, 5, 6])
sd3 = sd1 + sd2
self.assertEqual(sd3, deque([1, 2, 3, 4, 5, 6]))
self.assertIsInstance(sd3, Deque)


def test_multiply(self):
class Deque(deque): pass
d1 = Deque([1, 2, 3], maxlen=6)
d2 = d1 * 2
self.assertEqual(d2, deque([1, 2, 3, 1, 2, 3]))
self.assertIsInstance(d2, deque)


def test_copy(self):
import copy
class Deque(deque): pass
original = Deque([1, 2, 3], 6)
copy_instance = copy.copy(original)
self.assertEqual(original, copy_instance)
self.assertIsNot(original, copy_instance)
self.assertIsInstance(copy_instance, deque)


run_test(__name__)