From ad9217cfc5b4b8b23c296227e6f2b73b38f228e1 Mon Sep 17 00:00:00 2001 From: Aaron Robinson Date: Thu, 22 Aug 2024 20:40:47 -0700 Subject: [PATCH] Implement <, >, <=, >= (#156) * Implement <, >, <=, >= --- .../Python/PyObjectTests.cs | 47 +++++++++- src/CSnakes.Runtime/Python/PyObject.cs | 87 +++++++++++++++---- 2 files changed, 115 insertions(+), 19 deletions(-) diff --git a/src/CSnakes.Runtime.Tests/Python/PyObjectTests.cs b/src/CSnakes.Runtime.Tests/Python/PyObjectTests.cs index c33dae76..d07545d1 100644 --- a/src/CSnakes.Runtime.Tests/Python/PyObjectTests.cs +++ b/src/CSnakes.Runtime.Tests/Python/PyObjectTests.cs @@ -3,7 +3,6 @@ namespace CSnakes.Runtime.Tests.Python; public class PyObjectTests : RuntimeTestBase { - [Fact] public void TestToString() { @@ -63,7 +62,7 @@ public void TestObjectIsNone() [Fact] public void TestObjectIsSmallIntegers() { - // Small numbers are the same object in Python, weird implementation detail. + // Small numbers are the same object in Python, weird implementation detail. var obj1 = PyObject.From(42); var obj2 = PyObject.From(42); Assert.True(obj1!.Is(obj2!)); @@ -114,4 +113,48 @@ public void TestObjectNotEqualsCollection() Assert.True(obj1!.NotEquals(obj2)); Assert.True(obj1 != obj2); } + + [InlineData(null, null, false, false)] + [InlineData(0, null, false, false)] + [InlineData(null, 0, false, false)] + [InlineData(long.MaxValue, 0, false, true)] + [InlineData(long.MinValue, 0, true, false)] + [InlineData(0, long.MaxValue, true, false)] + [InlineData(0, long.MinValue, false, true)] + [InlineData((long)-1, 1, true, false)] + [InlineData(1, (long)-1, false, true)] + [InlineData("a", "b", true, false)] + [InlineData("b", "a", false, true)] + [InlineData(3.0, 3.2, true, false)] + [Theory] + public void TestObjectStrictInequality(object? o1, object? o2, bool expectedLT, bool expectedGT) + { + using var obj1 = o1 is null ? null : PyObject.From(o1); + using var obj2 = o2 is null ? null : PyObject.From(o2); + Assert.Equal(expectedLT, obj1 < obj2); + Assert.Equal(expectedGT, obj1 > obj2); + } + + [InlineData(null, null, true, true)] + [InlineData(0, null, false, false)] + [InlineData(null, 0, false, false)] + [InlineData(long.MaxValue, 0, false, true)] + [InlineData(long.MinValue, 0, true, false)] + [InlineData(0, long.MaxValue, true, false)] + [InlineData(0, long.MinValue, false, true)] + [InlineData((long)-1, 1, true, false)] + [InlineData(1, (long)-1, false, true)] + [InlineData("a", "b", true, false)] + [InlineData("b", "a", false, true)] + [InlineData(3.0, 3.2, true, false)] + [InlineData("b", "b", true, true)] + [InlineData(3.0, 3.0, true, true)] + [Theory] + public void TestObjectNotStrictInequality(object? o1, object? o2, bool expectedLT, bool expectedGT) + { + using var obj1 = o1 is null ? null : PyObject.From(o1); + using var obj2 = o2 is null ? null : PyObject.From(o2); + Assert.Equal(expectedLT, obj1 <= obj2); + Assert.Equal(expectedGT, obj1 >= obj2); + } } \ No newline at end of file diff --git a/src/CSnakes.Runtime/Python/PyObject.cs b/src/CSnakes.Runtime/Python/PyObject.cs index 98bfd07f..4d864064 100644 --- a/src/CSnakes.Runtime/Python/PyObject.cs +++ b/src/CSnakes.Runtime/Python/PyObject.cs @@ -183,14 +183,10 @@ public virtual bool Is(PyObject other) public override bool Equals(object? obj) { - if (obj is PyObject pyObj1) { + if (obj is PyObject pyObj1) { if (Is(pyObj1)) return true; - - using (GIL.Acquire()) - { - return CPythonAPI.PyObject_RichCompare(this, pyObj1, CPythonAPI.RichComparisonType.Equal); - } + return Compare(this, pyObj1, CPythonAPI.RichComparisonType.Equal); } return base.Equals(obj); } @@ -201,26 +197,83 @@ public bool NotEquals(object? obj) { if (Is(pyObj1)) return false; - - using (GIL.Acquire()) - { - return CPythonAPI.PyObject_RichCompare(this, pyObj1, CPythonAPI.RichComparisonType.NotEqual); - } + return Compare(this, pyObj1, CPythonAPI.RichComparisonType.NotEqual); } return !base.Equals(obj); } public static bool operator ==(PyObject? left, PyObject? right) { - if (left is null) - return right is null; - return left.Equals(right); + return (left, right) switch + { + (null, null) => true, + (_, null) => false, + (null, _) => false, + (_, _) => left.Equals(right), + }; } + public static bool operator !=(PyObject? left, PyObject? right) { - if (left is null) - return right is not null; - return left.NotEquals(right); + return (left, right) switch + { + (null, null) => false, + (_, null) => true, + (null, _) => true, + (_, _) => left.NotEquals(right), + }; + } + + public static bool operator <=(PyObject? left, PyObject? right) + { + return (left, right) switch + { + (null, null) => true, + (_, null) => false, + (null, _) => false, + (_, _) => left.Is(right) || Compare(left, right, CPythonAPI.RichComparisonType.LessThanEqual), + }; + } + + public static bool operator >=(PyObject? left, PyObject? right) + { + return (left, right) switch + { + (null, null) => true, + (_, null) => false, + (null, _) => false, + (_, _) => left.Is(right) || Compare(left, right, CPythonAPI.RichComparisonType.GreaterThanEqual), + }; + } + + public static bool operator <(PyObject? left, PyObject? right) + { + return (left, right) switch + { + (null, null) => false, + (_, null) => false, + (null, _) => false, + (_, _) => Compare(left, right, CPythonAPI.RichComparisonType.LessThan), + }; + } + + public static bool operator >(PyObject? left, PyObject? right) + { + return (left, right) switch + { + (null, null) => false, + (_, null) => false, + (null, _) => false, + (_, _) => Compare(left, right, CPythonAPI.RichComparisonType.GreaterThan), + }; + } + + private static bool Compare(PyObject left, PyObject right, CPythonAPI.RichComparisonType type) + { + using (GIL.Acquire()) + { + return CPythonAPI.PyObject_RichCompare(left, right, type); + } } public override int GetHashCode()