diff --git a/src/embed_tests/QCTest.cs b/src/embed_tests/QCTest.cs index 789834735..5fd2afd29 100644 --- a/src/embed_tests/QCTest.cs +++ b/src/embed_tests/QCTest.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using NUnit.Framework; using Python.Runtime; @@ -9,13 +7,24 @@ namespace Python.EmbeddingTest { class QCTests { + private static dynamic pythonSuperInitInt; + private static dynamic pythonSuperInitDefault; + private static dynamic pythonSuperInitNone; + private static dynamic pythonSuperInitNotCallingBase; + + private static dynamic withArgs_PythonSuperInitNotCallingBase; + private static dynamic withArgs_PythonSuperInitDefault; + private static dynamic withArgs_PythonSuperInitInt; + + private static dynamic pureCSharpConstruction; + private static dynamic containsTest; private static dynamic module; private static string testModule = @" from clr import AddReference AddReference(""System"") AddReference(""Python.EmbeddingTest"") -from Python.EmbeddingTest import Algo, Insight +from Python.EmbeddingTest import * class PythonModule(Algo): def TestA(self): try: @@ -28,6 +37,37 @@ def ContainsTest(key, collection): if key in collection.Keys: return True return False + +class WithArgs_PythonSuperInitNotCallingBase(SuperInit): + def __init__(self, jose): + return + +class WithArgs_PythonSuperInitDefault(SuperInit): + def __init__(self, jose): + super().__init__() + +class WithArgs_PythonSuperInitInt(SuperInit): + def __init__(self, jose): + super().__init__(jose) + +class PythonSuperInitNotCallingBase(SuperInit): + def __init__(self): + return + +class PythonSuperInitDefault(SuperInit): + def __init__(self): + super().__init__() + +class PythonSuperInitInt(SuperInit): + def __init__(self): + super().__init__(1) + +class PythonSuperInitNone(SuperInit): + def jose(self): + return 1 + +def PureCSharpConstruction(): + return SuperInit(1) "; [OneTimeSetUp] @@ -37,6 +77,17 @@ public void Setup() var pyModule = PyModule.FromString("module", testModule); containsTest = pyModule.GetAttr("ContainsTest"); module = pyModule.GetAttr("PythonModule").Invoke(); + + pythonSuperInitInt = pyModule.GetAttr("PythonSuperInitInt"); + pythonSuperInitDefault = pyModule.GetAttr("PythonSuperInitDefault"); + pythonSuperInitNone = pyModule.GetAttr("PythonSuperInitNone"); + pythonSuperInitNotCallingBase = pyModule.GetAttr("PythonSuperInitNotCallingBase"); + + withArgs_PythonSuperInitNotCallingBase = pyModule.GetAttr("WithArgs_PythonSuperInitNotCallingBase"); + withArgs_PythonSuperInitDefault = pyModule.GetAttr("WithArgs_PythonSuperInitDefault"); + withArgs_PythonSuperInitInt = pyModule.GetAttr("WithArgs_PythonSuperInitInt"); + + pureCSharpConstruction = pyModule.GetAttr("PureCSharpConstruction"); } [OneTimeTearDown] @@ -62,6 +113,98 @@ public void ContainsTest(string key, bool expected) var dic = new Dictionary { { "SPY", new object() } }; Assert.AreEqual(expected, (bool)containsTest(key, dic)); } + + [Test] + public void PureCSharpConstruction() + { + using (Py.GIL()) + { + var instance = pureCSharpConstruction(); + Assert.AreEqual(1, (int)instance.CalledInt); + Assert.AreEqual(1, (int)instance.CalledDefault); + } + } + + [Test] + public void WithArgs_NoBaseConstructorCall() + { + using (Py.GIL()) + { + var instance = withArgs_PythonSuperInitNotCallingBase(1); + Assert.AreEqual(0, (int)instance.CalledInt); + // we call the constructor always + Assert.AreEqual(1, (int)instance.CalledDefault); + } + } + + [Test] + public void WithArgs_IntConstructor() + { + using (Py.GIL()) + { + var instance = withArgs_PythonSuperInitInt(1); + Assert.AreEqual(1, (int)instance.CalledInt); + Assert.AreEqual(1, (int)instance.CalledDefault); + } + } + + [Test] + public void WithArgs_DefaultConstructor() + { + using (Py.GIL()) + { + var instance = withArgs_PythonSuperInitDefault(1); + Assert.AreEqual(0, (int)instance.CalledInt); + Assert.AreEqual(2, (int)instance.CalledDefault); + } + } + + [Test] + public void NoArgs_NoBaseConstructorCall() + { + using (Py.GIL()) + { + var instance = pythonSuperInitNotCallingBase(); + Assert.AreEqual(0, (int)instance.CalledInt); + // this is true because we call the default constructor always + Assert.AreEqual(1, (int)instance.CalledDefault); + } + } + + [Test] + public void NoArgs_IntConstructor() + { + using (Py.GIL()) + { + var instance = pythonSuperInitInt(); + Assert.AreEqual(1, (int)instance.CalledInt); + // this is true because we call the default constructor always + Assert.AreEqual(1, (int)instance.CalledDefault); + } + } + + [Test] + public void NoArgs_DefaultConstructor() + { + using (Py.GIL()) + { + var instance = pythonSuperInitNone(); + Assert.AreEqual(0, (int)instance.CalledInt); + Assert.AreEqual(2, (int)instance.CalledDefault); + } + } + + [Test] + public void NoArgs_NoConstructor() + { + using (Py.GIL()) + { + var instance = pythonSuperInitDefault.Invoke(); + + Assert.AreEqual(0, (int)instance.CalledInt); + Assert.AreEqual(2, (int)instance.CalledDefault); + } + } } public class Algo @@ -83,6 +226,20 @@ public void EmitInsights(params Insight[] insights) } + public class SuperInit + { + public int CalledInt { get; private set; } + public int CalledDefault { get; private set; } + public SuperInit(int a) + { + CalledInt++; + } + public SuperInit() + { + CalledDefault++; + } + } + public class Insight { public string info; diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj index a7726f2d7..6e3ca4966 100644 --- a/src/perf_tests/Python.PerformanceTests.csproj +++ b/src/perf_tests/Python.PerformanceTests.csproj @@ -13,7 +13,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + compile @@ -25,7 +25,7 @@ - + diff --git a/src/runtime/Properties/AssemblyInfo.cs b/src/runtime/Properties/AssemblyInfo.cs index ff96d4531..99a65c3d9 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.14")] -[assembly: AssemblyFileVersion("2.0.14")] +[assembly: AssemblyVersion("2.0.15")] +[assembly: AssemblyFileVersion("2.0.15")] diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 24d007d75..e2b0d8beb 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.14 + 2.0.15 false LICENSE https://github.com/pythonnet/pythonnet diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs index 5ba83c25e..28abd3cd9 100644 --- a/src/runtime/Types/ClassObject.cs +++ b/src/runtime/Types/ClassObject.cs @@ -15,12 +15,13 @@ namespace Python.Runtime [Serializable] internal class ClassObject : ClassBase { + private ConstructorInfo[] constructors; internal readonly int NumCtors = 0; internal ClassObject(Type tp) : base(tp) { - var _ctors = type.Value.GetConstructors(); - NumCtors = _ctors.Length; + constructors = type.Value.GetConstructors(); + NumCtors = constructors.Length; } @@ -110,8 +111,30 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo } object obj = FormatterServices.GetUninitializedObject(type); + var pythonObj = self.NewObjectToPython(obj, tp); - return self.NewObjectToPython(obj, tp); + try + { + var binder = new MethodBinder(); + for (int i = 0; i < self.constructors.Length; i++) + { + binder.AddMethod(self.constructors[i]); + } + + using var tuple = Runtime.PyTuple_New(0); + var binding = binder.Bind(pythonObj.Borrow(), tuple.Borrow(), null); + if (binding != null) + { + binding.info.Invoke(obj, BindingFlags.Default, null, binding.args, null); + } + } + catch (Exception) + { + Exceptions.Clear(); + // we try our best to call the base constructor but don't let it stop us + } + + return pythonObj; } protected virtual void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder)