Skip to content

Commit 3a9e2ef

Browse files
authored
Workaround for issue with concat and radd (#1478)
* Workaround for concat * Add test
1 parent 3965f0d commit 3a9e2ef

4 files changed

Lines changed: 44 additions & 1 deletion

File tree

Src/IronPython/Runtime/Binding/PythonProtocol.Operations.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,8 +705,10 @@ private static void GetOperatorMethods(DynamicMetaObject/*!*/[]/*!*/ types, Pyth
705705
rSlot = null;
706706
PythonType fParent, rParent;
707707

708+
bool isSequence = IsSequence(types[0]);
709+
708710
if (oper == PythonOperationKind.Multiply &&
709-
IsSequence(types[0]) &&
711+
isSequence &&
710712
!PythonOps.IsNonExtensibleNumericType(types[1].GetLimitType())) {
711713
// class M:
712714
// def __rmul__(self, other):
@@ -751,11 +753,19 @@ private static void GetOperatorMethods(DynamicMetaObject/*!*/[]/*!*/ types, Pyth
751753
}
752754
}
753755

756+
// TODO: this is incorrect - if the rslot returns NotImplemented then fslot is never called and a TypeError will be raised (https://github.com/IronLanguages/ironpython3/issues/1479)
754757
if (fParent != null && (rbinder.Success || rSlot != null) && rParent != fParent && rParent.IsSubclassOf(fParent)) {
755758
// Python says if x + subx and subx defines __r*__ we should call r*.
756759
fbinder = SlotOrFunction.Empty;
757760
fSlot = null;
758761
}
762+
763+
// TODO: this is incorrect - if the rslot returns NotImplemented then fslot is never called and a TypeError will be raised (https://github.com/IronLanguages/ironpython3/issues/560)
764+
if (oper == PythonOperationKind.Add && fParent != null && (rbinder.Success || rSlot != null) && rParent != fParent && isSequence) {
765+
// if the left operand is a sequence and the right operand defines __radd__, we should call __radd__.
766+
fbinder = SlotOrFunction.Empty;
767+
fSlot = null;
768+
}
759769
}
760770

761771
private static bool IsReverseOperator(PythonOperationKind oper) {

Src/IronPython/Runtime/Operations/StringOps.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,7 @@ public static string format_map(CodeContext/*!*/ context, [NotNone] string self,
13101310
#endregion
13111311

13121312
#region operators
1313+
13131314
[SpecialName]
13141315
public static string Add([NotNone] string self, [NotNone] string other) {
13151316
return self + other;
@@ -1325,6 +1326,13 @@ public static string Add(char self, [NotNone] string other) {
13251326
return self + other;
13261327
}
13271328

1329+
[SpecialName]
1330+
public static string Add([NotNone] string self, object? other) {
1331+
if (other is string s) return Add(self, s);
1332+
if (other is char c) return Add(self, c);
1333+
throw PythonOps.TypeError($"can only concatenate str (not \"{PythonOps.GetPythonTypeName(other)}\") to str");
1334+
}
1335+
13281336
[SpecialName]
13291337
public static string Mod(CodeContext/*!*/ context, [NotNone] string self, object? other) {
13301338
return StringFormatter.Format(context, self, other);

Src/IronPython/Runtime/PythonList.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ internal static PythonList FromArrayNoCopy(params object[] data)
225225
}
226226
}
227227

228+
public static PythonList operator +([NotNone] PythonList self, object? other) {
229+
if (other is PythonList l) return self + l;
230+
throw PythonOps.TypeError($"can only concatenate list (not \"{PythonOps.GetPythonTypeName(other)}\") to list");
231+
}
232+
228233
/// <summary>
229234
/// Gets a reasonable size for the addition of two arrays. We round
230235
/// to a power of two so that we usually have some extra space if

Tests/test_regressions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,5 +1667,25 @@ def __index__(self):
16671667
for code in ['b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q', 'n', 'N', 'P']:
16681668
self.assertEqual(_struct.Struct(code).pack(x(42))[idx], 42)
16691669

1670+
def test_ipy3_gh1475(self):
1671+
# The following test is adapted from https://github.com/IronLanguages/ironpython3/issues/560 and
1672+
# covers the issue described in https://github.com/IronLanguages/ironpython3/issues/1475
1673+
1674+
class M:
1675+
def __rmul__(self, other):
1676+
return 23
1677+
def __radd__(self, other):
1678+
return 64
1679+
1680+
m = M()
1681+
for seq in ([], (), "", b"", bytearray()):
1682+
with self.assertRaises(TypeError):
1683+
seq.__add__(m)
1684+
1685+
with self.assertRaises(TypeError):
1686+
seq.__mul__(m)
1687+
1688+
self.assertEqual(seq * m, 23)
1689+
self.assertEqual(seq + m, 64)
16701690

16711691
run_test(__name__)

0 commit comments

Comments
 (0)