diff --git a/src/core/IronPython.Modules/_collections.cs b/src/core/IronPython.Modules/_collections.cs index d58d66f1d..078029a4e 100644 --- a/src/core/IronPython.Modules/_collections.cs +++ b/src/core/IronPython.Modules/_collections.cs @@ -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; } @@ -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; @@ -961,7 +988,7 @@ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) { int res; CompareUtil.Push(this); try { - res = ((IStructuralEquatable)new PythonTuple(this)).GetHashCode(comparer); + res = ((IStructuralEquatable)PythonTuple.MakeTuple(GetObjectArray())).GetHashCode(comparer); } finally { CompareUtil.Pop(this); } diff --git a/tests/suite/test_deque.py b/tests/suite/test_deque.py index db9325655..e58fd32fc 100644 --- a/tests/suite/test_deque.py +++ b/tests/suite/test_deque.py @@ -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__)