Skip to content

Commit 47300d1

Browse files
Keep calling base managed constructor
1 parent e4aaed8 commit 47300d1

File tree

2 files changed

+178
-6
lines changed

2 files changed

+178
-6
lines changed

src/embed_tests/QCTest.cs

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
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+
1219
private static dynamic containsTest;
1320
private static dynamic module;
1421
private static string testModule = @"
1522
from clr import AddReference
1623
AddReference(""System"")
1724
AddReference(""Python.EmbeddingTest"")
18-
from Python.EmbeddingTest import Algo, Insight
25+
from Python.EmbeddingTest import *
1926
class PythonModule(Algo):
2027
def TestA(self):
2128
try:
@@ -28,6 +35,34 @@ def ContainsTest(key, collection):
2835
if key in collection.Keys:
2936
return True
3037
return False
38+
39+
class WithArgs_PythonSuperInitNotCallingBase(SuperInit):
40+
def __init__(self, jose):
41+
return
42+
43+
class WithArgs_PythonSuperInitDefault(SuperInit):
44+
def __init__(self, jose):
45+
super().__init__()
46+
47+
class WithArgs_PythonSuperInitInt(SuperInit):
48+
def __init__(self, jose):
49+
super().__init__(jose)
50+
51+
class PythonSuperInitNotCallingBase(SuperInit):
52+
def __init__(self):
53+
return
54+
55+
class PythonSuperInitDefault(SuperInit):
56+
def __init__(self):
57+
super().__init__()
58+
59+
class PythonSuperInitInt(SuperInit):
60+
def __init__(self):
61+
super().__init__(1)
62+
63+
class PythonSuperInitNone(SuperInit):
64+
def jose(self):
65+
return 1
3166
";
3267

3368
[OneTimeSetUp]
@@ -37,6 +72,15 @@ public void Setup()
3772
var pyModule = PyModule.FromString("module", testModule);
3873
containsTest = pyModule.GetAttr("ContainsTest");
3974
module = pyModule.GetAttr("PythonModule").Invoke();
75+
76+
pythonSuperInitInt = pyModule.GetAttr("PythonSuperInitInt");
77+
pythonSuperInitDefault = pyModule.GetAttr("PythonSuperInitDefault");
78+
pythonSuperInitNone = pyModule.GetAttr("PythonSuperInitNone");
79+
pythonSuperInitNotCallingBase = pyModule.GetAttr("PythonSuperInitNotCallingBase");
80+
81+
withArgs_PythonSuperInitNotCallingBase = pyModule.GetAttr("WithArgs_PythonSuperInitNotCallingBase");
82+
withArgs_PythonSuperInitDefault = pyModule.GetAttr("WithArgs_PythonSuperInitDefault");
83+
withArgs_PythonSuperInitInt = pyModule.GetAttr("WithArgs_PythonSuperInitInt");
4084
}
4185

4286
[OneTimeTearDown]
@@ -62,6 +106,87 @@ public void ContainsTest(string key, bool expected)
62106
var dic = new Dictionary<string, object> { { "SPY", new object() } };
63107
Assert.AreEqual(expected, (bool)containsTest(key, dic));
64108
}
109+
110+
[Test]
111+
public void WithArgs_NoBaseConstructorCall()
112+
{
113+
using (Py.GIL())
114+
{
115+
var instance = withArgs_PythonSuperInitNotCallingBase(1);
116+
// this is true because we call the constructor always
117+
Assert.IsTrue((bool)instance.CalledInt);
118+
Assert.IsFalse((bool)instance.CalledDefault);
119+
}
120+
}
121+
122+
[Test]
123+
public void WithArgs_IntConstructor()
124+
{
125+
using (Py.GIL())
126+
{
127+
var instance = withArgs_PythonSuperInitInt(1);
128+
Assert.IsTrue((bool)instance.CalledInt);
129+
Assert.IsFalse((bool)instance.CalledDefault);
130+
}
131+
}
132+
133+
[Test]
134+
public void WithArgs_DefaultConstructor()
135+
{
136+
using (Py.GIL())
137+
{
138+
var instance = withArgs_PythonSuperInitDefault(1);
139+
Assert.IsTrue((bool)instance.CalledInt);
140+
Assert.IsTrue((bool)instance.CalledDefault);
141+
}
142+
}
143+
144+
[Test]
145+
public void NoArgs_NoBaseConstructorCall()
146+
{
147+
using (Py.GIL())
148+
{
149+
var instance = pythonSuperInitNotCallingBase();
150+
Assert.IsFalse((bool)instance.CalledInt);
151+
// this is true because we call the default constructor always
152+
Assert.IsTrue((bool)instance.CalledDefault);
153+
}
154+
}
155+
156+
[Test]
157+
public void NoArgs_IntConstructor()
158+
{
159+
using (Py.GIL())
160+
{
161+
var instance = pythonSuperInitInt();
162+
Assert.IsTrue((bool)instance.CalledInt);
163+
// this is true because we call the default constructor always
164+
Assert.IsTrue((bool)instance.CalledDefault);
165+
}
166+
}
167+
168+
[Test]
169+
public void NoArgs_DefaultConstructor()
170+
{
171+
using (Py.GIL())
172+
{
173+
var instance = pythonSuperInitNone();
174+
Assert.IsFalse((bool)instance.CalledInt);
175+
Assert.IsTrue((bool)instance.CalledDefault);
176+
}
177+
}
178+
179+
[Test]
180+
public void NoArgs_NoConstructor()
181+
{
182+
using (Py.GIL())
183+
{
184+
var instance = pythonSuperInitDefault.Invoke();
185+
186+
Assert.IsFalse((bool)instance.CalledInt);
187+
Assert.IsTrue((bool)instance.CalledDefault);
188+
}
189+
}
65190
}
66191

67192
public class Algo
@@ -83,6 +208,20 @@ public void EmitInsights(params Insight[] insights)
83208

84209
}
85210

211+
public class SuperInit
212+
{
213+
public bool CalledInt { get; private set; }
214+
public bool CalledDefault { get; private set; }
215+
public SuperInit(int a)
216+
{
217+
CalledInt = true;
218+
}
219+
public SuperInit()
220+
{
221+
CalledDefault = true;
222+
}
223+
}
224+
86225
public class Insight
87226
{
88227
public string info;

src/runtime/Types/ClassObject.cs

Lines changed: 36 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,40 @@ 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+
// let's try to generate a binding using the args/kw we have
125+
var binding = binder.Bind(pythonObj.Borrow(), args, kw);
126+
if (binding != null)
127+
{
128+
binding.info.Invoke(obj, BindingFlags.Default, null, binding.args, null);
129+
}
130+
else
131+
{
132+
// if we didn't match any constructor let's fall back into the default constructor, no args
133+
using var tuple = Runtime.PyTuple_New(0);
134+
binding = binder.Bind(pythonObj.Borrow(), tuple.Borrow(), null);
135+
if(binding != null)
136+
{
137+
binding.info.Invoke(obj, BindingFlags.Default, null, binding.args, null);
138+
}
139+
}
140+
}
141+
catch (Exception)
142+
{
143+
Exceptions.Clear();
144+
// we try our best to call the base constructor but don't let it stop us
145+
}
146+
147+
return pythonObj;
115148
}
116149

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

0 commit comments

Comments
 (0)