diff --git a/src/embed_tests/ClassManagerTests.cs b/src/embed_tests/ClassManagerTests.cs index 0db0d282f..15da61e3b 100644 --- a/src/embed_tests/ClassManagerTests.cs +++ b/src/embed_tests/ClassManagerTests.cs @@ -1003,6 +1003,86 @@ def call(instance): } #endregion + + public enum TestEnum + { + FirstEnumValue, + SecondEnumValue, + ThirdEnumValue + } + + [Test] + public void EnumPythonOperationsCanBePerformedOnManagedEnum() + { + using (Py.GIL()) + { + var module = PyModule.FromString("EnumPythonOperationsCanBePerformedOnManagedEnum", $@" +from clr import AddReference +AddReference(""Python.EmbeddingTest"") + +from Python.EmbeddingTest import * + +def get_enum_values(): + return [x for x in ClassManagerTests.TestEnum] + +def count_enum_values(): + return len(ClassManagerTests.TestEnum) + +def is_enum_value_defined(value): + return value in ClassManagerTests.TestEnum + "); + + using var pyEnumValues = module.InvokeMethod("get_enum_values"); + var enumValues = pyEnumValues.As>(); + + var expectedEnumValues = Enum.GetValues(); + CollectionAssert.AreEquivalent(expectedEnumValues, enumValues); + + using var pyEnumCount = module.InvokeMethod("count_enum_values"); + var enumCount = pyEnumCount.As(); + Assert.AreEqual(expectedEnumValues.Length, enumCount); + + var validEnumValues = expectedEnumValues + .SelectMany(x => new object[] { x, (int)x, Enum.GetName(x.GetType(), x) }) + .Select(x => (x, true)); + var invalidEnumValues = new object[] { 5, "INVALID_ENUM_VALUE" }.Select(x => (x, false)); + + foreach (var (enumValue, isValid) in validEnumValues.Concat(invalidEnumValues)) + { + using var pyEnumValue = enumValue.ToPython(); + using var pyIsDefined = module.InvokeMethod("is_enum_value_defined", pyEnumValue); + var isDefined = pyIsDefined.As(); + Assert.AreEqual(isValid, isDefined, $"Failed for {enumValue} ({enumValue.GetType()})"); + } + } + } + + [Test] + public void EnumInterableOperationsNotSupportedForManagedNonEnumTypes() + { + using (Py.GIL()) + { + var module = PyModule.FromString("EnumInterableOperationsNotSupportedForManagedNonEnumTypes", $@" +from clr import AddReference +AddReference(""Python.EmbeddingTest"") + +from Python.EmbeddingTest import * + +def get_enum_values(): + return [x for x in ClassManagerTests] + +def count_enum_values(): + return len(ClassManagerTests) + +def is_enum_value_defined(): + return 1 in ClassManagerTests + "); + + Assert.Throws(() => module.InvokeMethod("get_enum_values")); + Assert.Throws(() => module.InvokeMethod("count_enum_values")); + Assert.Throws(() => module.InvokeMethod("is_enum_value_defined")); + } + } } public class NestedTestParent diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj index 540e18b66..99f447f56 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 126b2f62e..c8a43c43a 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.42")] -[assembly: AssemblyFileVersion("2.0.42")] +[assembly: AssemblyVersion("2.0.43")] +[assembly: AssemblyFileVersion("2.0.43")] diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 4ab951154..f1f77f9d7 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.42 + 2.0.43 false LICENSE https://github.com/pythonnet/pythonnet diff --git a/src/runtime/Types/MetaType.cs b/src/runtime/Types/MetaType.cs index 1543711f6..bfaced5f6 100644 --- a/src/runtime/Types/MetaType.cs +++ b/src/runtime/Types/MetaType.cs @@ -359,5 +359,79 @@ public static NewReference __subclasscheck__(BorrowedReference tp, BorrowedRefer { return DoInstanceCheck(tp, args, true); } + + /// + /// Standard iteration support Enums. This allows natural interation + /// over the available values an Enum defines. + /// + public static NewReference tp_iter(BorrowedReference tp) + { + if (!TryGetEnumType(tp, out var type)) + { + return default; + } + var values = Enum.GetValues(type); + return new Iterator(values.GetEnumerator(), type).Alloc(); + } + + /// + /// Implements __len__ for Enum types. + /// + public static int mp_length(BorrowedReference tp) + { + if (!TryGetEnumType(tp, out var type)) + { + return -1; + } + return Enum.GetValues(type).Length; + } + + /// + /// Implements __contains__ for Enum types. + /// + public static int sq_contains(BorrowedReference tp, BorrowedReference v) + { + if (!TryGetEnumType(tp, out var type)) + { + return -1; + } + + if (!Converter.ToManaged(v, type, out var enumValue, false) && + !Converter.ToManaged(v, typeof(int), out enumValue, false) && + !Converter.ToManaged(v, typeof(string), out enumValue, false)) + { + Exceptions.SetError(Exceptions.TypeError, + $"invalid parameter type for sq_contains: should be {Converter.GetTypeByAlias(v)}, found {type}"); + return -1; + } + + return Enum.IsDefined(type, enumValue) ? 1 : 0; + } + + private static bool TryGetEnumType(BorrowedReference tp, out Type type) + { + type = null; + var cb = GetManagedObject(tp) as ClassBase; + if (cb == null) + { + Exceptions.SetError(Exceptions.TypeError, "invalid object"); + return false; + } + + if (!cb.type.Valid) + { + Exceptions.SetError(Exceptions.TypeError, "invalid type"); + return false; + } + + if (!cb.type.Value.IsEnum) + { + Exceptions.SetError(Exceptions.TypeError, "uniterable type"); + return false; + } + + type = cb.type.Value; + return true; + } } }