Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
1 change: 1 addition & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@
- ([@gpetrou](https://github.com/gpetrou))
- Ehsan Iran-Nejad ([@eirannejad](https://github.com/eirannejad))
- ([@legomanww](https://github.com/legomanww))
- ([@gertdreyer](https://github.com/gertdreyer))
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].

### Fixed

- Fixed RecursionError for reverse operators on C# operable types from python. See #2240

## [3.0.3](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.3) - 2023-10-11

Expand Down
235 changes: 235 additions & 0 deletions src/embed_tests/TestOperator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class TestOperator
public void SetUp()
{
PythonEngine.Initialize();
OwnIntCodec.Setup();
}

[OneTimeTearDown]
Expand All @@ -23,6 +24,125 @@ public void Dispose()
PythonEngine.Shutdown();
}

// Mock Integer class to test math ops on non-native dotnet types
public struct OwnInt
Comment thread
gertdreyer marked this conversation as resolved.
Outdated
{
private int _value;

public int Num => _value;

public OwnInt()
{
_value = 0;
}

public OwnInt(int value)
{
_value = value;
}

public override int GetHashCode()
{
return unchecked(65535 + _value.GetHashCode());
}

public override bool Equals(object obj)
{
return obj is OwnInt @object &&
Num == @object.Num;
}

public static OwnInt operator -(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value - p2._value);
}

public static OwnInt operator +(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value + p2._value);
}

public static OwnInt operator *(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value * p2._value);
}

public static OwnInt operator /(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value / p2._value);
}

public static OwnInt operator %(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value % p2._value);
}

public static OwnInt operator ^(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value ^ p2._value);
}

public static bool operator <(OwnInt p1, OwnInt p2)
{
return p1._value < p2._value;
}

public static bool operator >(OwnInt p1, OwnInt p2)
{
return p1._value > p2._value;
}

public static bool operator ==(OwnInt p1, OwnInt p2)
{
return p1._value == p2._value;
}

public static bool operator !=(OwnInt p1, OwnInt p2)
{
return p1._value != p2._value;
}

public static OwnInt operator |(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value | p2._value);
}

public static OwnInt operator &(OwnInt p1, OwnInt p2)
{
return new OwnInt(p1._value & p2._value);
}

public static bool operator <=(OwnInt p1, OwnInt p2)
{
return p1._value <= p2._value;
}

public static bool operator >=(OwnInt p1, OwnInt p2)
{
return p1._value >= p2._value;
}
}

// Codec for mock class above.
public class OwnIntCodec : IPyObjectDecoder
{
public static void Setup()
{
PyObjectConversions.RegisterDecoder(new OwnIntCodec());
}

public bool CanDecode(PyType objectType, Type targetType)
{
return objectType.Name == "int" && targetType == typeof(OwnInt);
}

public bool TryDecode<T>(PyObject pyObj, out T value)
{
value = (T)(object)new OwnInt(pyObj.As<int>());
return true;
}
}

public class OperableObject
{
public int Num { get; set; }
Expand Down Expand Up @@ -524,6 +644,121 @@ public void ShiftOperatorOverloads()

c = a >> b.Num
assert c.Num == a.Num >> b.Num
");
}

[Test]
public void ReverseOperatorWithCodec()
{
string name = string.Format("{0}.{1}",
typeof(OwnInt).DeclaringType.Name,
typeof(OwnInt).Name);
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;

PythonEngine.Exec($@"
from {module} import *
cls = {name}
a = 2
b = cls(10)

c = a + b
assert c.Num == a + b.Num

c = a - b
assert c.Num == a - b.Num

c = a * b
assert c.Num == a * b.Num

c = a / b
assert c.Num == a // b.Num

c = a % b
assert c.Num == a % b.Num

c = a & b
assert c.Num == a & b.Num

c = a | b
assert c.Num == a | b.Num

c = a ^ b
assert c.Num == a ^ b.Num

c = a == b
assert c == (a == b.Num)

c = a != b
assert c == (a != b.Num)

c = a <= b
assert c == (a <= b.Num)

c = a >= b
assert c == (a >= b.Num)

c = a < b
assert c == (a < b.Num)

c = a > b
assert c == (a > b.Num)
");
}

[Test]
public void ForwardOperatorWithCodec()
{
string name = string.Format("{0}.{1}",
typeof(OwnInt).DeclaringType.Name,
typeof(OwnInt).Name);
string module = MethodBase.GetCurrentMethod().DeclaringType.Namespace;

PythonEngine.Exec($@"
from {module} import *
cls = {name}
a = cls(2)
b = 10
c = a + b
assert c.Num == a.Num + b

c = a - b
assert c.Num == a.Num - b

c = a * b
assert c.Num == a.Num * b

c = a / b
assert c.Num == a.Num // b

c = a % b
assert c.Num == a.Num % b

c = a & b
assert c.Num == a.Num & b

c = a | b
assert c.Num == a.Num | b

c = a ^ b
assert c.Num == a.Num ^ b

c = a == b
assert c == (a.Num == b)

c = a != b
assert c == (a.Num != b)

c = a <= b
assert c == (a.Num <= b)

c = a >= b
assert c == (a.Num >= b)

c = a < b
assert c == (a.Num < b)

c = a > b
assert c == (a.Num > b)
");
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/ClassManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl)
ci.members[pyName] = new MethodObject(type, name, forwardMethods).AllocObject();
// Only methods where only the right operand is the declaring type.
if (reverseMethods.Length > 0)
ci.members[pyNameReverse] = new MethodObject(type, name, reverseMethods).AllocObject();
ci.members[pyNameReverse] = new MethodObject(type, name, reverseMethods, argsReversed: true).AllocObject();
}
}

Expand Down
Loading