Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions src/perf_tests/Python.PerformanceTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
<PackageReference Include="quantconnect.pythonnet" Version="2.0.46" GeneratePathProperty="true">
<PackageReference Include="quantconnect.pythonnet" Version="2.0.47" GeneratePathProperty="true">
<IncludeAssets>compile</IncludeAssets>
</PackageReference>
</ItemGroup>
Expand All @@ -25,7 +25,7 @@
</Target>

<Target Name="CopyBaseline" AfterTargets="Build">
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.46\lib\net9.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.47\lib\net9.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
</Target>

<Target Name="CopyNewBuild" AfterTargets="Build">
Expand Down
5 changes: 5 additions & 0 deletions src/runtime/ClassManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
2 changes: 1 addition & 1 deletion src/runtime/Python.Runtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<RootNamespace>Python.Runtime</RootNamespace>
<AssemblyName>Python.Runtime</AssemblyName>
<PackageId>QuantConnect.pythonnet</PackageId>
<Version>2.0.46</Version>
<Version>2.0.47</Version>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>
Expand Down
85 changes: 48 additions & 37 deletions src/runtime/Types/ClassBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
/// <summary>
/// Get the result of a comparison operation based on the operator and the comparison result.
/// </summary>
/// <remarks>
/// This method is used to determine the result of a comparison operation, excluding equality and inequality.
/// </remarks>
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;

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/Types/DelegateObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public static NewReference tp_call(BorrowedReference ob, BorrowedReference args,
/// <summary>
/// Implements __cmp__ for reflected delegate types.
/// </summary>
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)
{
Expand Down
218 changes: 218 additions & 0 deletions src/runtime/Types/EnumObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
using System;
using System.Runtime.CompilerServices;

namespace Python.Runtime
{
/// <summary>
/// Managed class that provides the implementation for reflected enum types.
/// </summary>
[Serializable]
internal class EnumObject : ClassBase
{
internal EnumObject(Type type) : base(type)
{
}

/// <summary>
/// Standard comparison implementation for instances of enum types.
/// </summary>
public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op)
{
object rightInstance;
CLRObject leftClrObject;
int comparisonResult;

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, 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, 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);
}
}

/// <summary>
/// Tries comparing the give enum to the right operand by converting it to the appropriate type if possible
/// </summary>
/// <returns>True if the right operand was converted to a supported type and the comparison was performed successfully</returns>
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)
{
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;
}

#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

/// <summary>
/// We support comparing enums of different types by comparing their underlying values.
/// </summary>
[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
}
}
Loading
Loading