From 44f39daedc676ceca4d5e8b13fd6e3bd53a23d7c Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 13 Aug 2025 17:25:51 -0400 Subject: [PATCH 1/7] Refactor enums comparison operators for performance improvements --- src/runtime/ClassManager.cs | 5 + src/runtime/Types/ClassBase.cs | 85 ++++--- src/runtime/Types/EnumObject.cs | 223 +++++++++++++++++ src/runtime/Util/OpsHelper.cs | 423 -------------------------------- 4 files changed, 276 insertions(+), 460 deletions(-) create mode 100644 src/runtime/Types/EnumObject.cs diff --git a/src/runtime/ClassManager.cs b/src/runtime/ClassManager.cs index bf852112c..b88a6a6b6 100644 --- a/src/runtime/ClassManager.cs +++ b/src/runtime/ClassManager.cs @@ -220,6 +220,11 @@ internal static ClassBase CreateClass(Type type) impl = new LookUpObject(type); } + else if (type.IsEnum) + { + impl = new EnumObject(type); + } + else { impl = new ClassObject(type); diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs index ded315952..590c870b5 100644 --- a/src/runtime/Types/ClassBase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -156,42 +156,7 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc try { int cmp = co1Comp.CompareTo(co2Inst); - - BorrowedReference pyCmp; - if (cmp < 0) - { - if (op == Runtime.Py_LT || op == Runtime.Py_LE) - { - pyCmp = Runtime.PyTrue; - } - else - { - pyCmp = Runtime.PyFalse; - } - } - else if (cmp == 0) - { - if (op == Runtime.Py_LE || op == Runtime.Py_GE) - { - pyCmp = Runtime.PyTrue; - } - else - { - pyCmp = Runtime.PyFalse; - } - } - else - { - if (op == Runtime.Py_GE || op == Runtime.Py_GT) - { - pyCmp = Runtime.PyTrue; - } - else - { - pyCmp = Runtime.PyFalse; - } - } - return new NewReference(pyCmp); + return new NewReference(GetComparisonResult(op, cmp)); } catch (ArgumentException e) { @@ -202,7 +167,53 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc } } - private static bool TryGetSecondCompareOperandInstance(BorrowedReference left, BorrowedReference right, out CLRObject co1, out object co2Inst) + /// + /// Get the result of a comparison operation based on the operator and the comparison result. + /// + /// + /// This method is used to determine the result of a comparison operation, excluding equality and inequality. + /// + protected static BorrowedReference GetComparisonResult(int op, int comparisonResult) + { + BorrowedReference pyCmp; + if (comparisonResult < 0) + { + if (op == Runtime.Py_LT || op == Runtime.Py_LE) + { + pyCmp = Runtime.PyTrue; + } + else + { + pyCmp = Runtime.PyFalse; + } + } + else if (comparisonResult == 0) + { + if (op == Runtime.Py_LE || op == Runtime.Py_GE) + { + pyCmp = Runtime.PyTrue; + } + else + { + pyCmp = Runtime.PyFalse; + } + } + else + { + if (op == Runtime.Py_GE || op == Runtime.Py_GT) + { + pyCmp = Runtime.PyTrue; + } + else + { + pyCmp = Runtime.PyFalse; + } + } + + return pyCmp; + } + + protected static bool TryGetSecondCompareOperandInstance(BorrowedReference left, BorrowedReference right, out CLRObject co1, out object co2Inst) { co2Inst = null; diff --git a/src/runtime/Types/EnumObject.cs b/src/runtime/Types/EnumObject.cs new file mode 100644 index 000000000..37411530e --- /dev/null +++ b/src/runtime/Types/EnumObject.cs @@ -0,0 +1,223 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Python.Runtime +{ + /// + /// Managed class that provides the implementation for reflected enum types. + /// + [Serializable] + internal class EnumObject : ClassObject + { + private bool _isUnsigned; + + internal EnumObject(Type type) : base(type) + { + if (!type.IsEnum) + { + throw new ArgumentException($"{nameof(EnumObject)} can only handle Enum types. Received {type.Name}", nameof(type)); + } + + _isUnsigned = type.GetEnumUnderlyingType() == typeof(UInt64); + } + + /// + /// Standard comparison implementation for instances of enum types. + /// + public new static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) + { + object rightInstance; + CLRObject leftClrObject; + int comparisonResult; + var leftType = Runtime.PyObject_TYPE(ob); + var leftClass = (EnumObject)GetManagedObject(leftType)!; + + switch (op) + { + case Runtime.Py_EQ: + case Runtime.Py_NE: + var pytrue = Runtime.PyTrue; + var pyfalse = Runtime.PyFalse; + + // swap true and false for NE + if (op != Runtime.Py_EQ) + { + pytrue = Runtime.PyFalse; + pyfalse = Runtime.PyTrue; + } + + if (ob == other) + { + return new NewReference(pytrue); + } + + if (!TryGetSecondCompareOperandInstance(ob, other, out leftClrObject, out rightInstance)) + { + return new NewReference(pyfalse); + } + + if (rightInstance != null && + TryCompare(leftClrObject.inst as Enum, rightInstance, leftClass._isUnsigned, out comparisonResult) && + comparisonResult == 0) + { + return new NewReference(pytrue); + } + else + { + return new NewReference(pyfalse); + } + + case Runtime.Py_LT: + case Runtime.Py_LE: + case Runtime.Py_GT: + case Runtime.Py_GE: + if (!TryGetSecondCompareOperandInstance(ob, other, out leftClrObject, out rightInstance)) + { + return Exceptions.RaiseTypeError("Cannot get managed object"); + } + + if (rightInstance == null) + { + return Exceptions.RaiseTypeError($"Cannot compare {leftClrObject.inst.GetType()} to None"); + } + + try + { + if (!TryCompare(leftClrObject.inst as Enum, rightInstance, leftClass._isUnsigned, out comparisonResult)) + { + return Exceptions.RaiseTypeError($"Cannot compare {leftClrObject.inst.GetType()} with {rightInstance.GetType()}"); + } + + return new NewReference(GetComparisonResult(op, comparisonResult)); + } + catch (ArgumentException e) + { + return Exceptions.RaiseTypeError(e.Message); + } + + default: + return new NewReference(Runtime.PyNotImplemented); + } + } + + /// + /// Tries comparing the give enum to the right operand by converting it to the appropriate type if possible + /// + /// True if the right operand was converted to a supported type and the comparison was performed successfully + private static bool TryCompare(Enum left, object right, bool isUnsigned, out int result) + { + result = int.MinValue; + var conversionSuccessful = true; + // Same enum comparison: + if (left.GetType() == right.GetType()) + { + result = left.CompareTo(right); + } + // Comparison with other enum types + else if (right.GetType().IsEnum) + { + result = Compare(left, right as Enum, isUnsigned); + } + else if (right is double rightDouble) + { + result = Compare(left, rightDouble, isUnsigned); + } + else if (right is long rightLong) + { + result = Compare(left, rightLong, isUnsigned); + } + else if (right is ulong rightULong) + { + result = Compare(left, rightULong, isUnsigned); + } + else if (right is int rightInt) + { + result = Compare(left, rightInt, isUnsigned); + } + else if (right is uint rightUInt) + { + result = Compare(left, rightUInt, isUnsigned); + } + else if (right is string rightString) + { + result = left.ToString().CompareTo(rightString); + } + else + { + conversionSuccessful = false; + } + + return conversionSuccessful; + } + + #region Comparison against integers + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(long a, ulong b) + { + if (a < 0) return -1; + return ((ulong)a).CompareTo(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, long b, bool isUnsigned) + { + + if (isUnsigned) + { + return -Compare(b, Convert.ToUInt64(a)); + } + return Convert.ToInt64(a).CompareTo(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, ulong b, bool inUnsigned) + { + if (inUnsigned) + { + return Convert.ToUInt64(a).CompareTo(b); + } + return Compare(Convert.ToInt64(a), b); + } + + #endregion + + #region Comparison against doubles + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, double b, bool isUnsigned) + { + if (isUnsigned) + { + var uIntA = Convert.ToUInt64(a); + if (uIntA < b) return -1; + if (uIntA > b) return 1; + return 0; + } + + var intA = Convert.ToInt64(a); + if (intA < b) return -1; + if (intA > b) return 1; + return 0; + } + + #endregion + + #region Comparison against other enum types + + /// + /// We support comparing enums of different types by comparing their underlying values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, Enum b, bool isUnsigned) + { + if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) + { + return Compare(a, Convert.ToUInt64(b), isUnsigned); + } + return Compare(a, Convert.ToInt64(b), isUnsigned); + } + + #endregion + } +} diff --git a/src/runtime/Util/OpsHelper.cs b/src/runtime/Util/OpsHelper.cs index 89ce79e20..135a67163 100644 --- a/src/runtime/Util/OpsHelper.cs +++ b/src/runtime/Util/OpsHelper.cs @@ -156,428 +156,5 @@ public static double op_Division(double a, T b) } #endregion - - #region Int comparison operators - - public static bool op_Equality(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) == uvalue; - } - return Convert.ToInt64(a) == b; - } - - public static bool op_Equality(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b == uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) == b; - } - - public static bool op_Equality(long a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Equality(ulong a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, long b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(T a, ulong b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(long a, T b) - { - return !op_Equality(b, a); - } - - public static bool op_Inequality(ulong a, T b) - { - return !op_Equality(b, a); - } - - public static bool op_LessThan(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) > uvalue; - } - return Convert.ToInt64(a) < b; - } - - public static bool op_LessThan(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b > uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) < b; - } - - public static bool op_LessThan(long a, T b) - { - return op_GreaterThan(b, a); - } - - public static bool op_LessThan(ulong a, T b) - { - return op_GreaterThan(b, a); - } - - public static bool op_GreaterThan(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) < uvalue; - } - return Convert.ToInt64(a) > b; - } - - public static bool op_GreaterThan(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b < uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) > b; - } - - public static bool op_GreaterThan(long a, T b) - { - return op_LessThan(b, a); - } - - public static bool op_GreaterThan(ulong a, T b) - { - return op_LessThan(b, a); - } - - public static bool op_LessThanOrEqual(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) >= uvalue; - } - return Convert.ToInt64(a) <= b; - } - - public static bool op_LessThanOrEqual(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) <= b; - } - - public static bool op_LessThanOrEqual(long a, T b) - { - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_LessThanOrEqual(ulong a, T b) - { - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) <= uvalue; - } - return Convert.ToInt64(a) >= b; - } - - public static bool op_GreaterThanOrEqual(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b <= uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) >= b; - } - - public static bool op_GreaterThanOrEqual(long a, T b) - { - return op_LessThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(ulong a, T b) - { - return op_LessThanOrEqual(b, a); - } - - #endregion - - #region Double comparison operators - - public static bool op_Equality(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) == b; - } - return Convert.ToInt64(a) == b; - } - - public static bool op_Equality(double a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, double b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(double a, T b) - { - return !op_Equality(b, a); - } - - public static bool op_LessThan(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) < b; - } - return Convert.ToInt64(a) < b; - } - - public static bool op_LessThan(double a, T b) - { - return op_GreaterThan(b, a); - } - - public static bool op_GreaterThan(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) > b; - } - return Convert.ToInt64(a) > b; - } - - public static bool op_GreaterThan(double a, T b) - { - return op_LessThan(b, a); - } - - public static bool op_LessThanOrEqual(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) <= b; - } - return Convert.ToInt64(a) <= b; - } - - public static bool op_LessThanOrEqual(double a, T b) - { - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) >= b; - } - return Convert.ToInt64(a) >= b; - } - - public static bool op_GreaterThanOrEqual(double a, T b) - { - return op_LessThanOrEqual(b, a); - } - - #endregion - - #region String comparison operators - public static bool op_Equality(T a, string b) - { - return a.ToString().Equals(b, StringComparison.InvariantCultureIgnoreCase); - } - public static bool op_Equality(string a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, string b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(string a, T b) - { - return !op_Equality(b, a); - } - - #endregion - - #region Enum comparison operators - - public static bool op_Equality(T a, Enum b) - { - if (b == null) - { - return false; - } - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_Equality(a, Convert.ToUInt64(b)); - } - return op_Equality(a, Convert.ToInt64(b)); - } - - public static bool op_Equality(Enum a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, Enum b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(Enum a, T b) - { - return !op_Equality(b, a); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ThrowOnNull(object obj, string @operator) - { - if (obj == null) - { - using (Py.GIL()) - { - Exceptions.RaiseTypeError($"'{@operator}' not supported between instances of '{typeof(T).Name}' and null/None"); - PythonException.ThrowLastAsClrException(); - } - } - } - - public static bool op_LessThan(T a, Enum b) - { - ThrowOnNull(b, "<"); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_LessThan(a, Convert.ToUInt64(b)); - } - return op_LessThan(a, Convert.ToInt64(b)); - } - - public static bool op_LessThan(Enum a, T b) - { - ThrowOnNull(a, "<"); - return op_GreaterThan(b, a); - } - - public static bool op_GreaterThan(T a, Enum b) - { - ThrowOnNull(b, ">"); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_GreaterThan(a, Convert.ToUInt64(b)); - } - return op_GreaterThan(a, Convert.ToInt64(b)); - } - - public static bool op_GreaterThan(Enum a, T b) - { - ThrowOnNull(a, ">"); - return op_LessThan(b, a); - } - - public static bool op_LessThanOrEqual(T a, Enum b) - { - ThrowOnNull(b, "<="); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_LessThanOrEqual(a, Convert.ToUInt64(b)); - } - return op_LessThanOrEqual(a, Convert.ToInt64(b)); - } - - public static bool op_LessThanOrEqual(Enum a, T b) - { - ThrowOnNull(a, "<="); - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(T a, Enum b) - { - ThrowOnNull(b, ">="); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_GreaterThanOrEqual(a, Convert.ToUInt64(b)); - } - return op_GreaterThanOrEqual(a, Convert.ToInt64(b)); - } - - public static bool op_GreaterThanOrEqual(Enum a, T b) - { - ThrowOnNull(a, ">="); - return op_LessThanOrEqual(b, a); - } - - #endregion - - #region Object equality operators - - public static bool op_Equality(T a, object b) - { - return false; - } - - public static bool op_Equality(object a, T b) - { - return false; - } - - public static bool op_Inequality(T a, object b) - { - return true; - } - - public static bool op_Inequality(object a, T b) - { - return true; - } - - #endregion } } From ea4b2b6fc9e323a1ac3cf3cb7de07000e3fc8b36 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 13 Aug 2025 17:56:30 -0400 Subject: [PATCH 2/7] Minor change --- src/runtime/Types/EnumObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/Types/EnumObject.cs b/src/runtime/Types/EnumObject.cs index 37411530e..dc8f03343 100644 --- a/src/runtime/Types/EnumObject.cs +++ b/src/runtime/Types/EnumObject.cs @@ -7,7 +7,7 @@ namespace Python.Runtime /// Managed class that provides the implementation for reflected enum types. /// [Serializable] - internal class EnumObject : ClassObject + internal class EnumObject : ClassBase { private bool _isUnsigned; From 1869c7c341c0e683d5e12d5170f09d9b832f0f2d Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 13 Aug 2025 18:18:35 -0400 Subject: [PATCH 3/7] Minor change --- src/runtime/Types/DelegateObject.cs | 2 +- src/runtime/Types/EnumObject.cs | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/runtime/Types/DelegateObject.cs b/src/runtime/Types/DelegateObject.cs index 43a75aba7..a469e6a52 100644 --- a/src/runtime/Types/DelegateObject.cs +++ b/src/runtime/Types/DelegateObject.cs @@ -103,7 +103,7 @@ public static NewReference tp_call(BorrowedReference ob, BorrowedReference args, /// /// Implements __cmp__ for reflected delegate types. /// - public new static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) + public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) { if (op != Runtime.Py_EQ && op != Runtime.Py_NE) { diff --git a/src/runtime/Types/EnumObject.cs b/src/runtime/Types/EnumObject.cs index dc8f03343..cf27c6e57 100644 --- a/src/runtime/Types/EnumObject.cs +++ b/src/runtime/Types/EnumObject.cs @@ -13,24 +13,18 @@ internal class EnumObject : ClassBase internal EnumObject(Type type) : base(type) { - if (!type.IsEnum) - { - throw new ArgumentException($"{nameof(EnumObject)} can only handle Enum types. Received {type.Name}", nameof(type)); - } - _isUnsigned = type.GetEnumUnderlyingType() == typeof(UInt64); } /// /// Standard comparison implementation for instances of enum types. /// - public new static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) + public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) { object rightInstance; CLRObject leftClrObject; int comparisonResult; - var leftType = Runtime.PyObject_TYPE(ob); - var leftClass = (EnumObject)GetManagedObject(leftType)!; + EnumObject leftClass; switch (op) { @@ -56,6 +50,7 @@ internal EnumObject(Type type) : base(type) return new NewReference(pyfalse); } + leftClass = (EnumObject)GetManagedObject(Runtime.PyObject_TYPE(ob))!; if (rightInstance != null && TryCompare(leftClrObject.inst as Enum, rightInstance, leftClass._isUnsigned, out comparisonResult) && comparisonResult == 0) @@ -81,6 +76,7 @@ internal EnumObject(Type type) : base(type) return Exceptions.RaiseTypeError($"Cannot compare {leftClrObject.inst.GetType()} to None"); } + leftClass = (EnumObject)GetManagedObject(Runtime.PyObject_TYPE(ob))!; try { if (!TryCompare(leftClrObject.inst as Enum, rightInstance, leftClass._isUnsigned, out comparisonResult)) From b9d601cacdd840961102786d7c34009d587b9ec3 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 14 Aug 2025 09:15:29 -0400 Subject: [PATCH 4/7] Minor improvements --- src/runtime/Types/EnumObject.cs | 67 +++++++++++++++++---------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/src/runtime/Types/EnumObject.cs b/src/runtime/Types/EnumObject.cs index cf27c6e57..f3a4d9daa 100644 --- a/src/runtime/Types/EnumObject.cs +++ b/src/runtime/Types/EnumObject.cs @@ -9,11 +9,8 @@ namespace Python.Runtime [Serializable] internal class EnumObject : ClassBase { - private bool _isUnsigned; - internal EnumObject(Type type) : base(type) { - _isUnsigned = type.GetEnumUnderlyingType() == typeof(UInt64); } /// @@ -24,7 +21,6 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc object rightInstance; CLRObject leftClrObject; int comparisonResult; - EnumObject leftClass; switch (op) { @@ -50,9 +46,8 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc return new NewReference(pyfalse); } - leftClass = (EnumObject)GetManagedObject(Runtime.PyObject_TYPE(ob))!; if (rightInstance != null && - TryCompare(leftClrObject.inst as Enum, rightInstance, leftClass._isUnsigned, out comparisonResult) && + TryCompare(leftClrObject.inst as Enum, rightInstance, out comparisonResult) && comparisonResult == 0) { return new NewReference(pytrue); @@ -76,10 +71,9 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc return Exceptions.RaiseTypeError($"Cannot compare {leftClrObject.inst.GetType()} to None"); } - leftClass = (EnumObject)GetManagedObject(Runtime.PyObject_TYPE(ob))!; try { - if (!TryCompare(leftClrObject.inst as Enum, rightInstance, leftClass._isUnsigned, out comparisonResult)) + if (!TryCompare(leftClrObject.inst as Enum, rightInstance, out comparisonResult)) { return Exceptions.RaiseTypeError($"Cannot compare {leftClrObject.inst.GetType()} with {rightInstance.GetType()}"); } @@ -100,39 +94,22 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc /// Tries comparing the give enum to the right operand by converting it to the appropriate type if possible /// /// True if the right operand was converted to a supported type and the comparison was performed successfully - private static bool TryCompare(Enum left, object right, bool isUnsigned, out int result) + private static bool TryCompare(Enum left, object right, out int result) { result = int.MinValue; var conversionSuccessful = true; + var leftType = left.GetType(); + var rightType = right.GetType(); // Same enum comparison: - if (left.GetType() == right.GetType()) + if (leftType == rightType) { result = left.CompareTo(right); } // Comparison with other enum types - else if (right.GetType().IsEnum) - { - result = Compare(left, right as Enum, isUnsigned); - } - else if (right is double rightDouble) - { - result = Compare(left, rightDouble, isUnsigned); - } - else if (right is long rightLong) - { - result = Compare(left, rightLong, isUnsigned); - } - else if (right is ulong rightULong) - { - result = Compare(left, rightULong, isUnsigned); - } - else if (right is int rightInt) - { - result = Compare(left, rightInt, isUnsigned); - } - else if (right is uint rightUInt) + else if (rightType.IsEnum) { - result = Compare(left, rightUInt, isUnsigned); + var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); + result = Compare(left, right as Enum, leftIsUnsigned); } else if (right is string rightString) { @@ -140,7 +117,31 @@ private static bool TryCompare(Enum left, object right, bool isUnsigned, out int } else { - conversionSuccessful = false; + var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); + if (right is double rightDouble) + { + result = Compare(left, rightDouble, leftIsUnsigned); + } + else if (right is long rightLong) + { + result = Compare(left, rightLong, leftIsUnsigned); + } + else if (right is ulong rightULong) + { + result = Compare(left, rightULong, leftIsUnsigned); + } + else if (right is int rightInt) + { + result = Compare(left, rightInt, leftIsUnsigned); + } + else if (right is uint rightUInt) + { + result = Compare(left, rightUInt, leftIsUnsigned); + } + else + { + conversionSuccessful = false; + } } return conversionSuccessful; From e372a3e586da7af7841611c335f10782a95114d0 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 14 Aug 2025 09:53:57 -0400 Subject: [PATCH 5/7] Minor change --- src/runtime/Types/EnumObject.cs | 72 ++++++++++++++------------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/src/runtime/Types/EnumObject.cs b/src/runtime/Types/EnumObject.cs index f3a4d9daa..8d4331db6 100644 --- a/src/runtime/Types/EnumObject.cs +++ b/src/runtime/Types/EnumObject.cs @@ -99,49 +99,39 @@ private static bool TryCompare(Enum left, object right, out int result) result = int.MinValue; var conversionSuccessful = true; var leftType = left.GetType(); - var rightType = right.GetType(); - // Same enum comparison: - if (leftType == rightType) - { - result = left.CompareTo(right); - } - // Comparison with other enum types - else if (rightType.IsEnum) - { - var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); - result = Compare(left, right as Enum, leftIsUnsigned); - } - else if (right is string rightString) - { - result = left.ToString().CompareTo(rightString); - } - else + var leftIsUnsigned = () => leftType.GetEnumUnderlyingType() == typeof(UInt64); + + switch (right) { - var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); - if (right is double rightDouble) - { - result = Compare(left, rightDouble, leftIsUnsigned); - } - else if (right is long rightLong) - { - result = Compare(left, rightLong, leftIsUnsigned); - } - else if (right is ulong rightULong) - { - result = Compare(left, rightULong, leftIsUnsigned); - } - else if (right is int rightInt) - { - result = Compare(left, rightInt, leftIsUnsigned); - } - else if (right is uint rightUInt) - { - result = Compare(left, rightUInt, leftIsUnsigned); - } - else - { + case Enum when leftType == right.GetType(): + // Same enum type + result = left.CompareTo(right); + break; + case Enum rightEnum: + // Different enum type + result = Compare(left, rightEnum, leftIsUnsigned()); + break; + case string rightString: + result = left.ToString().CompareTo(rightString); + break; + case double rightDouble: + result = Compare(left, rightDouble, leftIsUnsigned()); + break; + case long rightLong: + result = Compare(left, rightLong, leftIsUnsigned()); + break; + case ulong rightULong: + result = Compare(left, rightULong, leftIsUnsigned()); + break; + case int rightInt: + result = Compare(left, (long)rightInt, leftIsUnsigned()); + break; + case uint rightUInt: + result = Compare(left, (ulong)rightUInt, leftIsUnsigned()); + break; + default: conversionSuccessful = false; - } + break; } return conversionSuccessful; From d67809fa58fa09bdda14e461a0610bbc42805185 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 14 Aug 2025 10:00:58 -0400 Subject: [PATCH 6/7] Cleanup --- src/runtime/Types/EnumObject.cs | 70 ++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/runtime/Types/EnumObject.cs b/src/runtime/Types/EnumObject.cs index 8d4331db6..8c146ff50 100644 --- a/src/runtime/Types/EnumObject.cs +++ b/src/runtime/Types/EnumObject.cs @@ -99,39 +99,47 @@ private static bool TryCompare(Enum left, object right, out int result) result = int.MinValue; var conversionSuccessful = true; var leftType = left.GetType(); - var leftIsUnsigned = () => leftType.GetEnumUnderlyingType() == typeof(UInt64); + var rightType = right.GetType(); - switch (right) + // Same enum comparison: + if (leftType == rightType) { - case Enum when leftType == right.GetType(): - // Same enum type - result = left.CompareTo(right); - break; - case Enum rightEnum: - // Different enum type - result = Compare(left, rightEnum, leftIsUnsigned()); - break; - case string rightString: - result = left.ToString().CompareTo(rightString); - break; - case double rightDouble: - result = Compare(left, rightDouble, leftIsUnsigned()); - break; - case long rightLong: - result = Compare(left, rightLong, leftIsUnsigned()); - break; - case ulong rightULong: - result = Compare(left, rightULong, leftIsUnsigned()); - break; - case int rightInt: - result = Compare(left, (long)rightInt, leftIsUnsigned()); - break; - case uint rightUInt: - result = Compare(left, (ulong)rightUInt, leftIsUnsigned()); - break; - default: - conversionSuccessful = false; - break; + result = left.CompareTo(right); + } + // Comparison with other enum types + else if (rightType.IsEnum) + { + var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); + result = Compare(left, right as Enum, leftIsUnsigned); + } + else if (right is string rightString) + { + result = left.ToString().CompareTo(rightString); + } + else + { + var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); + switch (right) + { + case double rightDouble: + result = Compare(left, rightDouble, leftIsUnsigned); + break; + case long rightLong: + result = Compare(left, rightLong, leftIsUnsigned); + break; + case ulong rightULong: + result = Compare(left, rightULong, leftIsUnsigned); + break; + case int rightInt: + result = Compare(left, (long)rightInt, leftIsUnsigned); + break; + case uint rightUInt: + result = Compare(left, (ulong)rightUInt, leftIsUnsigned); + break; + default: + conversionSuccessful = false; + break; + } } return conversionSuccessful; From acc8957770a79b248719a197385cc16c4ad15e77 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Thu, 14 Aug 2025 10:01:42 -0400 Subject: [PATCH 7/7] Update version to 2.0.47 --- src/perf_tests/Python.PerformanceTests.csproj | 4 ++-- src/runtime/Properties/AssemblyInfo.cs | 4 ++-- src/runtime/Python.Runtime.csproj | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj index 4ee604bf2..8ea99d9b2 100644 --- a/src/perf_tests/Python.PerformanceTests.csproj +++ b/src/perf_tests/Python.PerformanceTests.csproj @@ -13,7 +13,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + compile @@ -25,7 +25,7 @@ - + diff --git a/src/runtime/Properties/AssemblyInfo.cs b/src/runtime/Properties/AssemblyInfo.cs index a7c2c1a3d..4bada6682 100644 --- a/src/runtime/Properties/AssemblyInfo.cs +++ b/src/runtime/Properties/AssemblyInfo.cs @@ -4,5 +4,5 @@ [assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] [assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] -[assembly: AssemblyVersion("2.0.46")] -[assembly: AssemblyFileVersion("2.0.46")] +[assembly: AssemblyVersion("2.0.47")] +[assembly: AssemblyFileVersion("2.0.47")] diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 558466d26..b60b36e6b 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -5,7 +5,7 @@ Python.Runtime Python.Runtime QuantConnect.pythonnet - 2.0.46 + 2.0.47 false LICENSE https://github.com/pythonnet/pythonnet