From 9dd4179e7fefd684a54cf342b6e8c25335db1216 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 20 Feb 2026 11:56:36 -0400 Subject: [PATCH 1/3] Fix non generic method overload resolution --- src/embed_tests/TestMethodBinder.cs | 37 +++++++++++++++++++++++++++++ src/runtime/MethodBinder.cs | 2 ++ 2 files changed, 39 insertions(+) diff --git a/src/embed_tests/TestMethodBinder.cs b/src/embed_tests/TestMethodBinder.cs index 979592492..c410b026e 100644 --- a/src/embed_tests/TestMethodBinder.cs +++ b/src/embed_tests/TestMethodBinder.cs @@ -1402,6 +1402,31 @@ def call_method_with_enum(): Assert.AreEqual(DayOfWeek.Monday, CSharpModel.ProvidedArgument); } + [TestCase("call_non_generic_method", "GenericOverloadTestMethod")] + [TestCase("call_generic_method", "GenericOverloadTestMethod")] + public void ResolvesToGenericOnlyWhenExplicitlyCalled(string pythonFuncToCall, string expectedMethodCalled) + { + using var _ = Py.GIL(); + + var module = PyModule.FromString($"ResolvesToGenericOnlyWhenExplicitlyCalled_{pythonFuncToCall}", @$" +from clr import AddReference +AddReference(""System"") +from Python.EmbeddingTest import * + +def call_non_generic_method(): + return TestMethodBinder.CSharpModel.GenericOverloadTestMethod(TestMethodBinder.CSharpModel(), 'Test') + +def call_generic_method(): + return TestMethodBinder.CSharpModel.GenericOverloadTestMethod[TestMethodBinder.CSharpModel](TestMethodBinder.CSharpModel(), 'Test') +"); + + Assert.DoesNotThrow(() => + { + using var result = module.GetAttr(pythonFuncToCall).Invoke(); + }); + Assert.AreEqual(expectedMethodCalled, CSharpModel.LastFuncCalled); + } + // Used to test that we match this function with Py DateTime & Date Objects public static int GetMonth(DateTime test) { @@ -1636,6 +1661,18 @@ public static void TestAction3(CSharpModel model1, CSharpModel model2) } LastFuncCalled = "TestAction3"; } + + public static string GenericOverloadTestMethod(CSharpModel testArg1, string testArg2, decimal testArgs3 = 0m) + { + LastFuncCalled = "GenericOverloadTestMethod"; + return string.Empty; + } + + public static T GenericOverloadTestMethod(CSharpModel testArg1, string testArg2, decimal testArgs3 = 0m) + { + LastFuncCalled = "GenericOverloadTestMethod"; + return default; + } } public class TestImplicitConversion diff --git a/src/runtime/MethodBinder.cs b/src/runtime/MethodBinder.cs index 54fd33ff4..d88c84a18 100644 --- a/src/runtime/MethodBinder.cs +++ b/src/runtime/MethodBinder.cs @@ -780,6 +780,8 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe else { bestMatch = matchesTouse + .GroupBy(x => x.Method.IsGenericMethod) + .MinBy(x => x.Key) .GroupBy(x => x.KwargsMatched) .OrderByDescending(x => x.Key) .First() From 0f20ebf4090e628605e5d89d691f1fa8f2de8ad0 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 20 Feb 2026 16:53:00 -0400 Subject: [PATCH 2/3] Add test case --- src/embed_tests/TestMethodBinder.cs | 14 ++++++++++++++ src/runtime/MethodBinder.cs | 14 +++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/embed_tests/TestMethodBinder.cs b/src/embed_tests/TestMethodBinder.cs index c410b026e..49b982d08 100644 --- a/src/embed_tests/TestMethodBinder.cs +++ b/src/embed_tests/TestMethodBinder.cs @@ -1404,6 +1404,7 @@ def call_method_with_enum(): [TestCase("call_non_generic_method", "GenericOverloadTestMethod")] [TestCase("call_generic_method", "GenericOverloadTestMethod")] + [TestCase("call_generic_class_method", "GenericOverloadTestClass.GenericOverloadTestMethod")] public void ResolvesToGenericOnlyWhenExplicitlyCalled(string pythonFuncToCall, string expectedMethodCalled) { using var _ = Py.GIL(); @@ -1418,6 +1419,9 @@ def call_non_generic_method(): def call_generic_method(): return TestMethodBinder.CSharpModel.GenericOverloadTestMethod[TestMethodBinder.CSharpModel](TestMethodBinder.CSharpModel(), 'Test') + +def call_generic_class_method(): + return GenericOverloadTestClass[TestMethodBinder.CSharpModel].GenericOverloadTestMethod(TestMethodBinder.CSharpModel(), 'Test') "); Assert.DoesNotThrow(() => @@ -1821,4 +1825,14 @@ public enum SomeEnu B = 2, } } + + public class GenericOverloadTestClass + { + public static T GenericOverloadTestMethod(T testArg1, string testArg2, decimal testArgs3 = 0m) + { + TestMethodBinder.CSharpModel.LastFuncCalled = "GenericOverloadTestClass.GenericOverloadTestMethod"; + return default; + + } + } } diff --git a/src/runtime/MethodBinder.cs b/src/runtime/MethodBinder.cs index d88c84a18..1f62f73d7 100644 --- a/src/runtime/MethodBinder.cs +++ b/src/runtime/MethodBinder.cs @@ -780,15 +780,11 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe else { bestMatch = matchesTouse - .GroupBy(x => x.Method.IsGenericMethod) - .MinBy(x => x.Key) - .GroupBy(x => x.KwargsMatched) - .OrderByDescending(x => x.Key) - .First() - .GroupBy(x => x.ImplicitOperations) - .OrderBy(x => x.Key) - .First() - .MinBy(x => GetMatchedArgumentsPrecedence(x.MethodInformation, pyArgCount, kwArgDict?.Keys)); + .OrderBy(x => x.Method.IsGenericMethod) + .ThenByDescending(x => x.KwargsMatched) + .ThenBy(x => x.ImplicitOperations) + .ThenBy(x => GetMatchedArgumentsPrecedence(x.MethodInformation, pyArgCount, kwArgDict?.Keys)) + .First(); } var margs = bestMatch.ManagedArgs; From 42989a5c8b522589c8c03edf9e51d625aac22862 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Fri, 20 Feb 2026 18:22:25 -0400 Subject: [PATCH 3/3] Bump version to 2.0.53 --- src/runtime/Properties/AssemblyInfo.cs | 4 ++-- src/runtime/Python.Runtime.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/runtime/Properties/AssemblyInfo.cs b/src/runtime/Properties/AssemblyInfo.cs index 5e90074cf..b17e8cd57 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.52")] -[assembly: AssemblyFileVersion("2.0.52")] +[assembly: AssemblyVersion("2.0.53")] +[assembly: AssemblyFileVersion("2.0.53")] diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 85aae5de1..981767b9e 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.52 + 2.0.53 false LICENSE https://github.com/pythonnet/pythonnet