Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 21c5dcc

Browse files
committed
implemented dynamic equality and inequality for PyObject instances
fixed unhandled Python errors during comparison attempts fixes #1848
1 parent a49f3a8 commit 21c5dcc

File tree

4 files changed

+83
-41
lines changed

4 files changed

+83
-41
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ details about the cause of the failure
4545
able to access members that are part of the implementation class, but not the
4646
interface. Use the new `__implementation__` or `__raw_implementation__` properties to
4747
if you need to "downcast" to the implementation class.
48+
- BREAKING: `==` and `!=` operators on `PyObject` instances now use Python comparison
49+
(previously was equivalent to `object.ReferenceEquals(,)`)
4850
- BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition
4951
to the regular method return value (unless they are passed with `ref` or `out` keyword).
5052
- BREAKING: Drop support for the long-deprecated CLR.* prefix.

src/embed_tests/dynamic.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,28 @@ public void PassPyObjectInNet()
128128
Assert.IsTrue(sys.testattr1.Equals(sys.testattr2));
129129
}
130130

131+
// regression test for https://github.com/pythonnet/pythonnet/issues/1848
132+
[Test]
133+
public void EnumEquality()
134+
{
135+
using var scope = Py.CreateScope();
136+
scope.Exec(@"
137+
import enum
138+
139+
class MyEnum(enum.IntEnum):
140+
OK = 1
141+
ERROR = 2
142+
143+
def get_status():
144+
return MyEnum.OK
145+
"
146+
);
147+
148+
dynamic MyEnum = scope.Get("MyEnum");
149+
dynamic status = scope.Get("get_status").Invoke();
150+
Assert.IsTrue(status == MyEnum.OK);
151+
}
152+
131153
// regression test for https://github.com/pythonnet/pythonnet/issues/1680
132154
[Test]
133155
public void ForEach()

src/runtime/PythonTypes/PyObject.cs

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,12 +1075,9 @@ public virtual bool Equals(PyObject? other)
10751075
{
10761076
return true;
10771077
}
1078-
int r = Runtime.PyObject_Compare(this, other);
1079-
if (Exceptions.ErrorOccurred())
1080-
{
1081-
throw PythonException.ThrowLastAsClrException();
1082-
}
1083-
return r == 0;
1078+
int result = Runtime.PyObject_RichCompareBool(obj, other.obj, Runtime.Py_EQ);
1079+
if (result < 0) throw PythonException.ThrowLastAsClrException();
1080+
return result != 0;
10841081
}
10851082

10861083

@@ -1304,6 +1301,18 @@ public override bool TryConvert(ConvertBinder binder, out object? result)
13041301
return false;
13051302
}
13061303

1304+
private bool TryCompare(PyObject arg, int op, out object @out)
1305+
{
1306+
int result = Runtime.PyObject_RichCompareBool(this.obj, arg.obj, op);
1307+
@out = result != 0;
1308+
if (result < 0)
1309+
{
1310+
Exceptions.Clear();
1311+
return false;
1312+
}
1313+
return true;
1314+
}
1315+
13071316
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object? result)
13081317
{
13091318
using var _ = Py.GIL();
@@ -1352,32 +1361,29 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
13521361
res = Runtime.PyNumber_InPlaceXor(this.obj, ((PyObject)arg).obj);
13531362
break;
13541363
case ExpressionType.GreaterThan:
1355-
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) > 0;
1356-
return true;
1364+
return this.TryCompare((PyObject)arg, Runtime.Py_GT, out result);
13571365
case ExpressionType.GreaterThanOrEqual:
1358-
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) >= 0;
1359-
return true;
1366+
return this.TryCompare((PyObject)arg, Runtime.Py_GE, out result);
13601367
case ExpressionType.LeftShift:
13611368
res = Runtime.PyNumber_Lshift(this.obj, ((PyObject)arg).obj);
13621369
break;
13631370
case ExpressionType.LeftShiftAssign:
13641371
res = Runtime.PyNumber_InPlaceLshift(this.obj, ((PyObject)arg).obj);
13651372
break;
13661373
case ExpressionType.LessThan:
1367-
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) < 0;
1368-
return true;
1374+
return this.TryCompare((PyObject)arg, Runtime.Py_LT, out result);
13691375
case ExpressionType.LessThanOrEqual:
1370-
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) <= 0;
1371-
return true;
1376+
return this.TryCompare((PyObject)arg, Runtime.Py_LE, out result);
13721377
case ExpressionType.Modulo:
13731378
res = Runtime.PyNumber_Remainder(this.obj, ((PyObject)arg).obj);
13741379
break;
13751380
case ExpressionType.ModuloAssign:
13761381
res = Runtime.PyNumber_InPlaceRemainder(this.obj, ((PyObject)arg).obj);
13771382
break;
13781383
case ExpressionType.NotEqual:
1379-
result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) != 0;
1380-
return true;
1384+
return this.TryCompare((PyObject)arg, Runtime.Py_NE, out result);
1385+
case ExpressionType.Equal:
1386+
return this.TryCompare((PyObject)arg, Runtime.Py_EQ, out result);
13811387
case ExpressionType.Or:
13821388
res = Runtime.PyNumber_Or(this.obj, ((PyObject)arg).obj);
13831389
break;
@@ -1402,6 +1408,40 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
14021408
return true;
14031409
}
14041410

1411+
public static bool operator ==(PyObject? a, PyObject? b)
1412+
{
1413+
if (a is null && b is null)
1414+
{
1415+
return true;
1416+
}
1417+
if (a is null || b is null)
1418+
{
1419+
return false;
1420+
}
1421+
1422+
using var _ = Py.GIL();
1423+
int result = Runtime.PyObject_RichCompareBool(a.obj, b.obj, Runtime.Py_EQ);
1424+
if (result < 0) throw PythonException.ThrowLastAsClrException();
1425+
return result != 0;
1426+
}
1427+
1428+
public static bool operator !=(PyObject? a, PyObject? b)
1429+
{
1430+
if (a is null && b is null)
1431+
{
1432+
return false;
1433+
}
1434+
if (a is null || b is null)
1435+
{
1436+
return true;
1437+
}
1438+
1439+
using var _ = Py.GIL();
1440+
int result = Runtime.PyObject_RichCompareBool(a.obj, b.obj, Runtime.Py_NE);
1441+
if (result < 0) throw PythonException.ThrowLastAsClrException();
1442+
return result != 0;
1443+
}
1444+
14051445
// Workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=41509
14061446
// See https://github.com/pythonnet/pythonnet/pull/219
14071447
internal static object? CheckNone(PyObject pyObj)
@@ -1436,14 +1476,17 @@ public override bool TryUnaryOperation(UnaryOperationBinder binder, out object?
14361476
case ExpressionType.Not:
14371477
r = Runtime.PyObject_Not(this.obj);
14381478
result = r == 1;
1479+
if (r == -1) Exceptions.Clear();
14391480
return r != -1;
14401481
case ExpressionType.IsFalse:
14411482
r = Runtime.PyObject_IsTrue(this.obj);
14421483
result = r == 0;
1484+
if (r == -1) Exceptions.Clear();
14431485
return r != -1;
14441486
case ExpressionType.IsTrue:
14451487
r = Runtime.PyObject_IsTrue(this.obj);
14461488
result = r == 1;
1489+
if (r == -1) Exceptions.Clear();
14471490
return r != -1;
14481491
case ExpressionType.Decrement:
14491492
case ExpressionType.Increment:

src/runtime/Runtime.cs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -962,31 +962,6 @@ internal static IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args)
962962

963963
internal static int PyObject_RichCompareBool(BorrowedReference value1, BorrowedReference value2, int opid) => Delegates.PyObject_RichCompareBool(value1, value2, opid);
964964

965-
internal static int PyObject_Compare(BorrowedReference value1, BorrowedReference value2)
966-
{
967-
int res;
968-
res = PyObject_RichCompareBool(value1, value2, Py_LT);
969-
if (-1 == res)
970-
return -1;
971-
else if (1 == res)
972-
return -1;
973-
974-
res = PyObject_RichCompareBool(value1, value2, Py_EQ);
975-
if (-1 == res)
976-
return -1;
977-
else if (1 == res)
978-
return 0;
979-
980-
res = PyObject_RichCompareBool(value1, value2, Py_GT);
981-
if (-1 == res)
982-
return -1;
983-
else if (1 == res)
984-
return 1;
985-
986-
Exceptions.SetError(Exceptions.SystemError, "Error comparing objects");
987-
return -1;
988-
}
989-
990965

991966
internal static int PyObject_IsInstance(BorrowedReference ob, BorrowedReference type) => Delegates.PyObject_IsInstance(ob, type);
992967

0 commit comments

Comments
 (0)