Skip to content

Commit 5aab127

Browse files
Merge pull request #67 from Martin-Molinero/bug-constructor-default-call
Bug constructor default call
2 parents e4aaed8 + 6edaf09 commit 5aab127

File tree

5 files changed

+191
-11
lines changed

5 files changed

+191
-11
lines changed

src/embed_tests/QCTest.cs

Lines changed: 160 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
53
using NUnit.Framework;
64
using Python.Runtime;
75

86
namespace Python.EmbeddingTest
97
{
108
class QCTests
119
{
10+
private static dynamic pythonSuperInitInt;
11+
private static dynamic pythonSuperInitDefault;
12+
private static dynamic pythonSuperInitNone;
13+
private static dynamic pythonSuperInitNotCallingBase;
14+
15+
private static dynamic withArgs_PythonSuperInitNotCallingBase;
16+
private static dynamic withArgs_PythonSuperInitDefault;
17+
private static dynamic withArgs_PythonSuperInitInt;
18+
19+
private static dynamic pureCSharpConstruction;
20+
1221
private static dynamic containsTest;
1322
private static dynamic module;
1423
private static string testModule = @"
1524
from clr import AddReference
1625
AddReference(""System"")
1726
AddReference(""Python.EmbeddingTest"")
18-
from Python.EmbeddingTest import Algo, Insight
27+
from Python.EmbeddingTest import *
1928
class PythonModule(Algo):
2029
def TestA(self):
2130
try:
@@ -28,6 +37,37 @@ def ContainsTest(key, collection):
2837
if key in collection.Keys:
2938
return True
3039
return False
40+
41+
class WithArgs_PythonSuperInitNotCallingBase(SuperInit):
42+
def __init__(self, jose):
43+
return
44+
45+
class WithArgs_PythonSuperInitDefault(SuperInit):
46+
def __init__(self, jose):
47+
super().__init__()
48+
49+
class WithArgs_PythonSuperInitInt(SuperInit):
50+
def __init__(self, jose):
51+
super().__init__(jose)
52+
53+
class PythonSuperInitNotCallingBase(SuperInit):
54+
def __init__(self):
55+
return
56+
57+
class PythonSuperInitDefault(SuperInit):
58+
def __init__(self):
59+
super().__init__()
60+
61+
class PythonSuperInitInt(SuperInit):
62+
def __init__(self):
63+
super().__init__(1)
64+
65+
class PythonSuperInitNone(SuperInit):
66+
def jose(self):
67+
return 1
68+
69+
def PureCSharpConstruction():
70+
return SuperInit(1)
3171
";
3272

3373
[OneTimeSetUp]
@@ -37,6 +77,17 @@ public void Setup()
3777
var pyModule = PyModule.FromString("module", testModule);
3878
containsTest = pyModule.GetAttr("ContainsTest");
3979
module = pyModule.GetAttr("PythonModule").Invoke();
80+
81+
pythonSuperInitInt = pyModule.GetAttr("PythonSuperInitInt");
82+
pythonSuperInitDefault = pyModule.GetAttr("PythonSuperInitDefault");
83+
pythonSuperInitNone = pyModule.GetAttr("PythonSuperInitNone");
84+
pythonSuperInitNotCallingBase = pyModule.GetAttr("PythonSuperInitNotCallingBase");
85+
86+
withArgs_PythonSuperInitNotCallingBase = pyModule.GetAttr("WithArgs_PythonSuperInitNotCallingBase");
87+
withArgs_PythonSuperInitDefault = pyModule.GetAttr("WithArgs_PythonSuperInitDefault");
88+
withArgs_PythonSuperInitInt = pyModule.GetAttr("WithArgs_PythonSuperInitInt");
89+
90+
pureCSharpConstruction = pyModule.GetAttr("PureCSharpConstruction");
4091
}
4192

4293
[OneTimeTearDown]
@@ -62,6 +113,98 @@ public void ContainsTest(string key, bool expected)
62113
var dic = new Dictionary<string, object> { { "SPY", new object() } };
63114
Assert.AreEqual(expected, (bool)containsTest(key, dic));
64115
}
116+
117+
[Test]
118+
public void PureCSharpConstruction()
119+
{
120+
using (Py.GIL())
121+
{
122+
var instance = pureCSharpConstruction();
123+
Assert.AreEqual(1, (int)instance.CalledInt);
124+
Assert.AreEqual(1, (int)instance.CalledDefault);
125+
}
126+
}
127+
128+
[Test]
129+
public void WithArgs_NoBaseConstructorCall()
130+
{
131+
using (Py.GIL())
132+
{
133+
var instance = withArgs_PythonSuperInitNotCallingBase(1);
134+
Assert.AreEqual(0, (int)instance.CalledInt);
135+
// we call the constructor always
136+
Assert.AreEqual(1, (int)instance.CalledDefault);
137+
}
138+
}
139+
140+
[Test]
141+
public void WithArgs_IntConstructor()
142+
{
143+
using (Py.GIL())
144+
{
145+
var instance = withArgs_PythonSuperInitInt(1);
146+
Assert.AreEqual(1, (int)instance.CalledInt);
147+
Assert.AreEqual(1, (int)instance.CalledDefault);
148+
}
149+
}
150+
151+
[Test]
152+
public void WithArgs_DefaultConstructor()
153+
{
154+
using (Py.GIL())
155+
{
156+
var instance = withArgs_PythonSuperInitDefault(1);
157+
Assert.AreEqual(0, (int)instance.CalledInt);
158+
Assert.AreEqual(2, (int)instance.CalledDefault);
159+
}
160+
}
161+
162+
[Test]
163+
public void NoArgs_NoBaseConstructorCall()
164+
{
165+
using (Py.GIL())
166+
{
167+
var instance = pythonSuperInitNotCallingBase();
168+
Assert.AreEqual(0, (int)instance.CalledInt);
169+
// this is true because we call the default constructor always
170+
Assert.AreEqual(1, (int)instance.CalledDefault);
171+
}
172+
}
173+
174+
[Test]
175+
public void NoArgs_IntConstructor()
176+
{
177+
using (Py.GIL())
178+
{
179+
var instance = pythonSuperInitInt();
180+
Assert.AreEqual(1, (int)instance.CalledInt);
181+
// this is true because we call the default constructor always
182+
Assert.AreEqual(1, (int)instance.CalledDefault);
183+
}
184+
}
185+
186+
[Test]
187+
public void NoArgs_DefaultConstructor()
188+
{
189+
using (Py.GIL())
190+
{
191+
var instance = pythonSuperInitNone();
192+
Assert.AreEqual(0, (int)instance.CalledInt);
193+
Assert.AreEqual(2, (int)instance.CalledDefault);
194+
}
195+
}
196+
197+
[Test]
198+
public void NoArgs_NoConstructor()
199+
{
200+
using (Py.GIL())
201+
{
202+
var instance = pythonSuperInitDefault.Invoke();
203+
204+
Assert.AreEqual(0, (int)instance.CalledInt);
205+
Assert.AreEqual(2, (int)instance.CalledDefault);
206+
}
207+
}
65208
}
66209

67210
public class Algo
@@ -83,6 +226,20 @@ public void EmitInsights(params Insight[] insights)
83226

84227
}
85228

229+
public class SuperInit
230+
{
231+
public int CalledInt { get; private set; }
232+
public int CalledDefault { get; private set; }
233+
public SuperInit(int a)
234+
{
235+
CalledInt++;
236+
}
237+
public SuperInit()
238+
{
239+
CalledDefault++;
240+
}
241+
}
242+
86243
public class Insight
87244
{
88245
public string info;

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.14" GeneratePathProperty="true">
16+
<PackageReference Include="quantconnect.pythonnet" Version="2.0.15" 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.14\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
28+
<Copy SourceFiles="$(NuGetPackageRoot)quantconnect.pythonnet\2.0.15\lib\net5.0\Python.Runtime.dll" DestinationFolder="$(OutDir)baseline" />
2929
</Target>
3030

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

src/runtime/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
[assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
55
[assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")]
66

7-
[assembly: AssemblyVersion("2.0.14")]
8-
[assembly: AssemblyFileVersion("2.0.14")]
7+
[assembly: AssemblyVersion("2.0.15")]
8+
[assembly: AssemblyFileVersion("2.0.15")]

src/runtime/Python.Runtime.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<RootNamespace>Python.Runtime</RootNamespace>
66
<AssemblyName>Python.Runtime</AssemblyName>
77
<PackageId>QuantConnect.pythonnet</PackageId>
8-
<Version>2.0.14</Version>
8+
<Version>2.0.15</Version>
99
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1010
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1111
<RepositoryUrl>https://github.com/pythonnet/pythonnet</RepositoryUrl>

src/runtime/Types/ClassObject.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ namespace Python.Runtime
1515
[Serializable]
1616
internal class ClassObject : ClassBase
1717
{
18+
private ConstructorInfo[] constructors;
1819
internal readonly int NumCtors = 0;
1920

2021
internal ClassObject(Type tp) : base(tp)
2122
{
22-
var _ctors = type.Value.GetConstructors();
23-
NumCtors = _ctors.Length;
23+
constructors = type.Value.GetConstructors();
24+
NumCtors = constructors.Length;
2425
}
2526

2627

@@ -110,8 +111,30 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo
110111
}
111112

112113
object obj = FormatterServices.GetUninitializedObject(type);
114+
var pythonObj = self.NewObjectToPython(obj, tp);
113115

114-
return self.NewObjectToPython(obj, tp);
116+
try
117+
{
118+
var binder = new MethodBinder();
119+
for (int i = 0; i < self.constructors.Length; i++)
120+
{
121+
binder.AddMethod(self.constructors[i]);
122+
}
123+
124+
using var tuple = Runtime.PyTuple_New(0);
125+
var binding = binder.Bind(pythonObj.Borrow(), tuple.Borrow(), null);
126+
if (binding != null)
127+
{
128+
binding.info.Invoke(obj, BindingFlags.Default, null, binding.args, null);
129+
}
130+
}
131+
catch (Exception)
132+
{
133+
Exceptions.Clear();
134+
// we try our best to call the base constructor but don't let it stop us
135+
}
136+
137+
return pythonObj;
115138
}
116139

117140
protected virtual void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder)

0 commit comments

Comments
 (0)