Skip to content

Commit a06fc32

Browse files
Move __init__ calling to ClassDerived.tp_init
1 parent 0dda85a commit a06fc32

File tree

2 files changed

+75
-20
lines changed

2 files changed

+75
-20
lines changed

src/embed_tests/TestRuntime.cs

+31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using NUnit.Framework;
33
using Python.Runtime;
4+
using System.Linq;
45

56
namespace Python.EmbeddingTest
67
{
@@ -88,5 +89,35 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test()
8889

8990
Runtime.Runtime.Py_Finalize();
9091
}
92+
93+
[Test]
94+
public static void PyInit_Called_once() {
95+
PythonEngine.Initialize();
96+
97+
var locals = new PyDict();
98+
99+
100+
PythonEngine.Exec(@"
101+
import System
102+
calls = []
103+
104+
class X(System.Object):
105+
__namespace__ = ""PyTest""
106+
107+
def __init__(self, calls, *args):
108+
print(args)
109+
calls.append(args)
110+
111+
x = X(calls, 1, 2)
112+
print(calls)
113+
", null, locals.Handle);
114+
var calls = locals.GetItem("calls");
115+
Assert.AreEqual(1, calls.Length());
116+
Assert.AreEqual(2, calls[0].Length());
117+
Assert.AreEqual(1, calls[0][0].As<int>());
118+
Assert.AreEqual(2, calls[0][1].As<int>());
119+
PythonEngine.Shutdown();
120+
121+
}
91122
}
92123
}

src/runtime/classderived.cs

+44-20
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,50 @@ internal ClassDerivedObject(Type tp) : base(tp)
5555
return Converter.ToPython(obj, cls.GetType());
5656
}
5757

58+
/// <summary>
59+
/// Implements __init__ for derived classes of reflected classes.
60+
/// </summary>
61+
public new static int tp_init(IntPtr ob, IntPtr args, IntPtr kw)
62+
{
63+
var disposeList = new List<PyObject>();
64+
try
65+
{
66+
var self = (CLRObject)GetManagedObject(ob);
67+
68+
// call __init__
69+
using (var pyself = new PyObject(self.pyHandle))
70+
using (var pynone = new PyObject(Runtime.PyNone))
71+
{
72+
PyObject init = pyself.GetAttr("__init__", pynone);
73+
int pynargs = Runtime.PyTuple_Size(args);
74+
if (init.Handle != Runtime.PyNone)
75+
{
76+
// if __init__ hasn't been overridden then it will be a managed object
77+
ManagedType managedMethod = ManagedType.GetManagedObject(init.Handle);
78+
if (null == managedMethod)
79+
{
80+
var pyargs = new PyObject[pynargs];
81+
for (var i = 0; i < pynargs; ++i)
82+
{
83+
pyargs[i] = new PyObject(Runtime.PyTuple_GetItem(args, i));
84+
}
85+
86+
disposeList.Add(init.Invoke(pyargs));
87+
}
88+
}
89+
}
90+
91+
return 0;
92+
}
93+
finally
94+
{
95+
foreach (PyObject x in disposeList)
96+
{
97+
x?.Dispose();
98+
}
99+
}
100+
}
101+
58102
public new static void tp_dealloc(IntPtr ob)
59103
{
60104
var self = (CLRObject)GetManagedObject(ob);
@@ -829,26 +873,6 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec
829873
Runtime.XIncref(Runtime.PyNone);
830874
var pynone = new PyObject(Runtime.PyNone);
831875
disposeList.Add(pynone);
832-
833-
// call __init__
834-
PyObject init = pyself.GetAttr("__init__", pynone);
835-
disposeList.Add(init);
836-
if (init.Handle != Runtime.PyNone)
837-
{
838-
// if __init__ hasn't been overridden then it will be a managed object
839-
ManagedType managedMethod = ManagedType.GetManagedObject(init.Handle);
840-
if (null == managedMethod)
841-
{
842-
var pyargs = new PyObject[args.Length];
843-
for (var i = 0; i < args.Length; ++i)
844-
{
845-
pyargs[i] = new PyObject(Converter.ToPython(args[i], args[i]?.GetType()));
846-
disposeList.Add(pyargs[i]);
847-
}
848-
849-
disposeList.Add(init.Invoke(pyargs));
850-
}
851-
}
852876
}
853877
finally
854878
{

0 commit comments

Comments
 (0)