Skip to content

Commit 8fb227a

Browse files
authored
Merge pull request #92 from jhonabreul/bug-error-set-even-with-matched-overload
Fix type instance conversion
2 parents 72d052e + 65ad1b9 commit 8fb227a

File tree

7 files changed

+298
-64
lines changed

7 files changed

+298
-64
lines changed

src/embed_tests/TestConverter.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,27 @@ public void PrimitiveIntConversion()
450450
var testInt = pyValue.As<int>();
451451
Assert.AreEqual(testInt , 10);
452452
}
453+
454+
[TestCase(typeof(Type), true)]
455+
[TestCase(typeof(string), false)]
456+
[TestCase(typeof(TestCSharpModel), false)]
457+
public void NoErrorSetWhenFailingToConvertClassType(Type type, bool shouldConvert)
458+
{
459+
using var _ = Py.GIL();
460+
461+
var module = PyModule.FromString("CallsCorrectOverloadWithoutErrors", @"
462+
from clr import AddReference
463+
AddReference(""System"")
464+
AddReference(""Python.EmbeddingTest"")
465+
from Python.EmbeddingTest import *
466+
467+
class TestPythonModel(TestCSharpModel):
468+
pass
469+
");
470+
var testPythonModelClass = module.GetAttr("TestPythonModel");
471+
Assert.AreEqual(shouldConvert, Converter.ToManaged(testPythonModelClass, type, out var result, setError: false));
472+
Assert.IsFalse(Exceptions.ErrorOccurred());
473+
}
453474
}
454475

455476
public interface IGetList
@@ -461,4 +482,8 @@ public class GetListImpl : IGetList
461482
{
462483
public List<string> GetList() => new() { "testing" };
463484
}
485+
486+
public class TestCSharpModel
487+
{
488+
}
464489
}

src/embed_tests/TestMethodBinder.cs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,164 @@ from Python.EmbeddingTest import *
740740
"));
741741
}
742742

743+
public class OverloadsTestClass
744+
{
745+
746+
public string Method1(string positionalArg, decimal namedArg1 = 1.2m, int namedArg2 = 123)
747+
{
748+
Console.WriteLine("1");
749+
return "Method1 Overload 1";
750+
}
751+
752+
public string Method1(decimal namedArg1 = 1.2m, int namedArg2 = 123)
753+
{
754+
Console.WriteLine("2");
755+
return "Method1 Overload 2";
756+
}
757+
758+
// ----
759+
760+
public string Method2(string arg1, int arg2, decimal arg3, decimal kwarg1 = 1.1m, bool kwarg2 = false, string kwarg3 = "")
761+
{
762+
return "Method2 Overload 1";
763+
}
764+
765+
public string Method2(string arg1, int arg2, decimal kwarg1 = 1.1m, bool kwarg2 = false, string kwarg3 = "")
766+
{
767+
return "Method2 Overload 2";
768+
}
769+
770+
// ----
771+
772+
public string Method3(string arg1, int arg2, float arg3, float kwarg1 = 1.1f, bool kwarg2 = false, string kwarg3 = "")
773+
{
774+
return "Method3 Overload 1";
775+
}
776+
777+
public string Method3(string arg1, int arg2, float kwarg1 = 1.1f, bool kwarg2 = false, string kwarg3 = "")
778+
{
779+
return "Method3 Overload 2";
780+
}
781+
782+
// ----
783+
784+
public string ImplicitConversionSameArgumentCount(string symbol, int quantity, float trailingAmount, bool trailingAsPercentage, string tag = "")
785+
{
786+
return "ImplicitConversionSameArgumentCount 1";
787+
}
788+
789+
public string ImplicitConversionSameArgumentCount(string symbol, decimal quantity, decimal trailingAmount, bool trailingAsPercentage, string tag = "")
790+
{
791+
return "ImplicitConversionSameArgumentCount 2";
792+
}
793+
794+
public string ImplicitConversionSameArgumentCount2(string symbol, int quantity, float trailingAmount, bool trailingAsPercentage, string tag = "")
795+
{
796+
return "ImplicitConversionSameArgumentCount2 1";
797+
}
798+
799+
public string ImplicitConversionSameArgumentCount2(string symbol, float quantity, float trailingAmount, bool trailingAsPercentage, string tag = "")
800+
{
801+
return "ImplicitConversionSameArgumentCount2 2";
802+
}
803+
804+
public string ImplicitConversionSameArgumentCount2(string symbol, decimal quantity, float trailingAmount, bool trailingAsPercentage, string tag = "")
805+
{
806+
return "ImplicitConversionSameArgumentCount2 2";
807+
}
808+
}
809+
810+
[TestCase("Method1('abc', namedArg1=10, namedArg2=321)", "Method1 Overload 1")]
811+
[TestCase("Method1('abc', namedArg1=12.34, namedArg2=321)", "Method1 Overload 1")]
812+
[TestCase("Method2(\"SPY\", 10, 123, kwarg1=1, kwarg2=True)", "Method2 Overload 1")]
813+
[TestCase("Method2(\"SPY\", 10, 123.34, kwarg1=1.23, kwarg2=True)", "Method2 Overload 1")]
814+
[TestCase("Method3(\"SPY\", 10, 123.34, kwarg1=1.23, kwarg2=True)", "Method3 Overload 1")]
815+
public void SelectsRightOverloadWithNamedParameters(string methodCallCode, string expectedResult)
816+
{
817+
using var _ = Py.GIL();
818+
819+
dynamic module = PyModule.FromString("SelectsRightOverloadWithNamedParameters", @$"
820+
821+
def call_method(instance):
822+
return instance.{methodCallCode}
823+
");
824+
825+
var instance = new OverloadsTestClass();
826+
var result = module.call_method(instance).As<string>();
827+
828+
Assert.AreEqual(expectedResult, result);
829+
}
830+
831+
[TestCase("ImplicitConversionSameArgumentCount", "10", "ImplicitConversionSameArgumentCount 1")]
832+
[TestCase("ImplicitConversionSameArgumentCount", "10.1", "ImplicitConversionSameArgumentCount 2")]
833+
[TestCase("ImplicitConversionSameArgumentCount2", "10", "ImplicitConversionSameArgumentCount2 1")]
834+
[TestCase("ImplicitConversionSameArgumentCount2", "10.1", "ImplicitConversionSameArgumentCount2 2")]
835+
public void DisambiguatesOverloadWithSameArgumentCountAndImplicitConversion(string methodName, string quantity, string expectedResult)
836+
{
837+
using var _ = Py.GIL();
838+
839+
dynamic module = PyModule.FromString("DisambiguatesOverloadWithSameArgumentCountAndImplicitConversion", @$"
840+
def call_method(instance):
841+
return instance.{methodName}(""SPY"", {quantity}, 123.4, trailingAsPercentage=True)
842+
");
843+
844+
var instance = new OverloadsTestClass();
845+
var result = module.call_method(instance).As<string>();
846+
847+
Assert.AreEqual(expectedResult, result);
848+
}
849+
850+
public class CSharpClass
851+
{
852+
public string CalledMethodMessage { get; private set; }
853+
854+
public void Method()
855+
{
856+
CalledMethodMessage = "Overload 1";
857+
}
858+
859+
public void Method(string stringArgument, decimal decimalArgument = 1.2m)
860+
{
861+
CalledMethodMessage = "Overload 2";
862+
}
863+
864+
public void Method(PyObject typeArgument, decimal decimalArgument = 1.2m)
865+
{
866+
CalledMethodMessage = "Overload 3";
867+
}
868+
}
869+
870+
[Test]
871+
public void CallsCorrectOverloadWithoutErrors()
872+
{
873+
using var _ = Py.GIL();
874+
875+
var module = PyModule.FromString("CallsCorrectOverloadWithoutErrors", @"
876+
from clr import AddReference
877+
AddReference(""System"")
878+
AddReference(""Python.EmbeddingTest"")
879+
from Python.EmbeddingTest import *
880+
881+
class PythonModel(TestMethodBinder.CSharpModel):
882+
pass
883+
884+
def call_method(instance):
885+
instance.Method(PythonModel, decimalArgument=1.234)
886+
");
887+
888+
var instance = new CSharpClass();
889+
using var pyInstance = instance.ToPython();
890+
891+
Assert.DoesNotThrow(() =>
892+
{
893+
module.GetAttr("call_method").Invoke(pyInstance);
894+
});
895+
896+
Assert.AreEqual("Overload 3", instance.CalledMethodMessage);
897+
898+
Assert.IsFalse(Exceptions.ErrorOccurred());
899+
}
900+
743901

744902
// Used to test that we match this function with Py DateTime & Date Objects
745903
public static int GetMonth(DateTime test)

src/perf_tests/Python.PerformanceTests.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1414
</PackageReference>
1515
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.*" />
16-
<PackageReference Include="quantconnect.pythonnet" Version="2.0.36" GeneratePathProperty="true">
16+
<PackageReference Include="quantconnect.pythonnet" Version="2.0.37" GeneratePathProperty="true">
1717
<IncludeAssets>compile</IncludeAssets>
1818
</PackageReference>
1919
</ItemGroup>
@@ -25,7 +25,7 @@
2525
</Target>
2626

2727
<Target Name="CopyBaseline" AfterTargets="Build">
28-
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.36\lib\net6.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
28+
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.37\lib\net6.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
2929
</Target>
3030

3131
<Target Name="CopyNewBuild" AfterTargets="Build">

src/runtime/Converter.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,13 @@ internal static bool ToManagedValue(BorrowedReference value, Type obType,
473473
}
474474
if (mt is ClassBase cb)
475475
{
476-
if (!cb.type.Valid || !obType.IsInstanceOfType(cb.type.Value))
476+
// The value being converted is a class type, so it will only succeed if it's being converted into a Type
477+
if (obType != typeof(Type))
478+
{
479+
return false;
480+
}
481+
482+
if (!cb.type.Valid)
477483
{
478484
Exceptions.SetError(Exceptions.TypeError, cb.type.DeletedMessage);
479485
return false;
@@ -845,8 +851,21 @@ internal static bool ToPrimitive(BorrowedReference value, Type obType, out objec
845851
}
846852

847853
case TypeCode.Boolean:
848-
result = Runtime.PyObject_IsTrue(value) != 0;
854+
if (value == Runtime.PyTrue)
855+
{
856+
result = true;
857+
return true;
858+
}
859+
if (value == Runtime.PyFalse)
860+
{
861+
result = false;
849862
return true;
863+
}
864+
if (setError)
865+
{
866+
goto type_error;
867+
}
868+
return false;
850869

851870
case TypeCode.Byte:
852871
{

0 commit comments

Comments
 (0)