8000 Fix implicit conversion method resolution · saaib/pythonnet@b98085c · GitHub
[go: up one dir, main page]

Skip to content

Commit b98085c

Browse files
Fix implicit conversion method resolution
- Fixes for implicit conversions which did not work and fix for methodBinder method resolution, will prioritize methods that do not required implicit conversions - Adding unit tests
1 parent 9e29755 commit b98085c

File tree

8 files changed

+307
-88
lines changed

8 files changed

+307
-88
lines changed

src/embed_tests/Python.EmbeddingTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
<Compile Include="TestCustomMarshal.cs" />
8989
<Compile Include="TestExample.cs" />
9090
<Compile Include="TestFinalizer.cs" />
91+
<Compile Include="TestMethodBinder.cs" />
9192
<Compile Include="TestPyAnsiString.cs" />
9293
<Compile Include="TestPyFloat.cs" />
9394
<Compile Include="TestPyInt.cs" />

src/embed_tests/TestMethodBinder.cs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
using NUnit.Framework;
2+
using Python.Runtime;
3+
4+
namespace Python.EmbeddingTest
5+
{
6+
public class TestMethodBinder
7+
{
8+
[OneTimeSetUp]
9+
public void SetUp()
10+
{
11+
PythonEngine.Initialize();
12+
}
13+
14+
[OneTimeTearDown]
15+
public void Dispose()
16+
{
17+
PythonEngine.Shutdown();
18+
}
19+
20+
[Test]
21+
public void ImplicitConversionToString()
22+
{
23+
// create instance of python model
24+
dynamic pyMethodCall = PythonEngine.ModuleFromString("module", @"
25+
from clr import AddReference
26+
AddReference(""System"")
27+
AddReference(""Python.EmbeddingTest"")
28+
29+
from Python.EmbeddingTest import *
30+
31+
class PythonModel(TestMethodBinder.CSharpModel):
32+
def MethodCall(self):
33+
return self.OnlyString(TestMethodBinder.TestImplicitConversion())
34+
").GetAttr("PythonModel").Invoke();
35+
36+
using (Py.GIL())
37+
{
38+
var data = (string)pyMethodCall.MethodCall();
39+
// we assert implicit conversion took place
40+
Assert.AreEqual("OnlyString impl: implicit to string", data);
41+
}
42+
}
43+
44+
[Test]
45+
public void ImplicitConversionToClass()
46+
{
47+
// create instance of python model
48+
dynamic pyMethodCall = PythonEngine.ModuleFromString("module", @"
49+
from clr import AddReference
50+
AddReference(""System"")
51+
AddReference(""Python.EmbeddingTest"")
52+
53+
from Python.EmbeddingTest import *
54+
55+
class PythonModel(TestMethodBinder.CSharpModel):
56+
def MethodCall(self):
57+
return self.OnlyClass('input string')
58+
").GetAttr("PythonModel").Invoke();
59+
60+
using (Py.GIL())
61+
{
62+
var data = (string)pyMethodCall.MethodCall();
63+
// we assert implicit conversion took place
64+
Assert.AreEqual("OnlyClass impl", data);
65+
}
66+
}
67+
< A93C code>68+
[Test]
69+
public void WillAvoidUsingImplicitConversionIfPossible_String()
70+
{
71+
// create instance of python model
72+
dynamic pyMethodCall = PythonEngine.ModuleFromString("module", @"
73+
from clr import AddReference
74+
AddReference(""System"")
75+
AddReference(""Python.EmbeddingTest"")
76+
77+
from Python.EmbeddingTest import *
78+
79+
class PythonModel(TestMethodBinder.CSharpModel):
80+
def MethodCall(self):
81+
return self.InvokeModel('input string')
82+
").GetAttr("PythonModel").Invoke();
83+
84+
using (Py.GIL())
85+
{
86+
var data = (string)pyMethodCall.MethodCall();
87+
// we assert no implicit conversion took place
88+
Assert.AreEqual("string impl: input string", data);
89+
}
90+
}
91+
92+
[Test]
93+
public void WillAvoidUsingImplicitConversionIfPossible_Class()
94+
{
95+
// create instance of python model
96+
dynamic pyMethodCall = PythonEngine.ModuleFromString("module", @"< F438 /span>
97+
from clr import AddReference
98+
AddReference(""System"")
99+
AddReference(""Python.EmbeddingTest"")
100+
101+
from Python.EmbeddingTest import *
102+
103+
class PythonModel(TestMethodBinder.CSharpModel):
104+
def MethodCall(self):
105+
return self.InvokeModel(TestMethodBinder.TestImplicitConversion())
106+
").GetAttr("PythonModel").Invoke();
107+
108+
using (Py.GIL())
109+
{
110+
var data = (string)pyMethodCall.MethodCall();
111+
// we assert no implicit conversion took place
112+
Assert.AreEqual("TestImplicitConversion impl", data);
113+
}
114+
}
115+
116+
public class CSharpModel
117+
{
118+
public virtual string OnlyClass(TestImplicitConversion data)
119+
{
120+
return "OnlyClass impl";
121+
}
122+
123+
public virtual string OnlyString(string data)
124+
{
125+
return "OnlyString impl: " + data;
126+
}
127+
128+
public virtual string InvokeModel(string data)
129+
{
130+
return "string impl: " + data;
131+
}
132+
133+
public virtual string InvokeModel(TestImplicitConversion data)
134+
{
135+
return "TestImplicitConversion impl";
136+
}
137+
}
138+
139+
public class TestImplicitConversion
140+
{
141+
public static implicit operator string(TestImplicitConversion symbol)
142+
{
143+
return "implicit to string";
144+
}
145+
public static implicit operator TestImplicitConversion(string symbol)
146+
{
147+
return new TestImplicitConversion();
148+
}
149+
}
150+
}
151+
}

src/runtime/classobject.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ internal ClassObject(Type tp) : base(tp)
3131
/// </summary>
3232
internal IntPtr GetDocString()
3333
{
34-
MethodBase[] methods = binder.GetMethods();
34+
var methods = binder.GetMethods();
3535
var str = "";
36-
foreach (MethodBase t in methods)
36+
foreach (var t in methods)
3737
{
3838
if (str.Length > 0)
3939
{

src/runtime/constructorbinding.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,10 @@ public static IntPtr tp_repr(IntPtr ob)
119119
Runtime.XIncref(self.repr);
120120
return self.repr;
121121
}
122-
MethodBase[] methods = self.ctorBinder.GetMethods();
122+
var methods = self.ctorBinder.GetMethods();
123123
string name = self.type.FullName;
124124
var doc = "";
125-
foreach (MethodBase t in methods)
125+
foreach (var t in methods)
126126
{
127127
if (doc.Length > 0)
128128
{

src/runtime/converter.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,17 @@ internal static bool ToManagedValue(IntPtr value, Type obType,
397397
result = tmp;
398398
return true;
399399
}
400+
else
401+
{
402+
var type = tmp.GetType();
403+
// check implicit conversions that receive tmp type and return obType
404+
var conversionMethod = type.GetMethod("op_Implicit", new[] { type });
405+
if (conversionMethod != null && conversionMethod.ReturnType == obType)
406+
{
407+
result = conversionMethod.Invoke(null, new[] { tmp });
408+
return true;
409+
}
410+
}
400411
Exceptions.SetError(Exceptions.TypeError, $"value cannot be converted to {obType}");
401412
return false;
402413
}

src/runtime/indexer.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,14 @@ internal void SetItem(IntPtr inst, IntPtr args)
5656

5757
internal bool NeedsDefaultArgs(IntPtr args)
5858
{
59-
int pynargs = Runtime.PyTuple_Size(args);
60-
MethodBase[] methods = SetterBinder.GetMethods();
61-
if (methods.Length == 0)
59+
var methods = SetterBinder.GetMethods();
60+
if (methods.Count == 0)
6261
{
6362
return false;
6463
}
64+
int pynargs = Runtime.PyTuple_Size(args);
6565

66-
MethodBase mi = methods[0];
67-
ParameterInfo[] pi = mi.GetParameters();
66+
var pi = methods[0].ParameterInfo;
6867
// need to subtract one for the value
6968
int clrnargs = pi.Length - 1;
7069
if (pynargs == clrnargs || pynargs > clrnargs)
@@ -98,9 +97,8 @@ internal IntPtr GetDefaultArgs(IntPtr args)
9897
int pynargs = Runtime.PyTuple_Size(args);
9998

10099
// Get the default arg tuple
101-
MethodBase[] methods = SetterBinder.GetMethods();
102-
MethodBase mi = methods[0];
103-
ParameterInfo[] pi = mi.GetParameters();
100+
var methods = SetterBinder.GetMethods();
101+
ParameterInfo[] pi = methods[0].ParameterInfo;
104102
int clrnargs = pi.Length - 1;
105103
IntPtr defaultArgs = Runtime.PyTuple_New(clrnargs - pynargs);
106104
for (var i = 0; i < clrnargs - pynargs; i++)

0 commit comments

Comments
 (0)
0