Skip to content

[WIP] Drop tp_call implementation from metatype #496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
attribute (#481)
- Fixed conversion of 'float' and 'double' values (#486)
- Fixed 'clrmethod' for python 2 (#492)
- Fixed double calling of constructor when deriving from .NET class (#495)


## [2.3.0][] - 2017-03-11
Expand Down
55 changes: 28 additions & 27 deletions src/runtime/classderived.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -55,6 +55,33 @@ internal ClassDerivedObject(Type tp) : base(tp)
return Converter.ToPython(obj, cls.GetType());
}

public new static int tp_init(IntPtr obj, IntPtr args, IntPtr kw)
{
Runtime.XIncref(obj);
Runtime.XIncref(Runtime.PyNone);
using (var pyself = new PyObject(obj))
using (var pynone = new PyObject(Runtime.PyNone))
using (var init = pyself.GetAttr("__init__", pynone))
{
if (init.Handle != Runtime.PyNone)
{
// if __init__ hasn't been overridden then it will be a managed object
if (GetManagedObject(init.Handle) == null)
{
Runtime.XIncref(args);
Runtime.XIncref(kw);
using (var args_ = new PyTuple(args))
using (var kw_ = new PyDict(kw))
using (var res = init.Invoke(args_, kw_))
{
}
}
}
}

return 0;
}

public new static void tp_dealloc(IntPtr ob)
{
var self = (CLRObject)GetManagedObject(ob);
Expand Down Expand Up @@ -822,33 +849,7 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec
FieldInfo fi = obj.GetType().GetField("__pyobj__");
fi.SetValue(obj, self);

Runtime.XIncref(self.pyHandle);
var pyself = new PyObject(self.pyHandle);
disposeList.Add(pyself);

Runtime.XIncref(Runtime.PyNone);
var pynone = new PyObject(Runtime.PyNone);
disposeList.Add(pynone);

// call __init__
PyObject init = pyself.GetAttr("__init__", pynone);
disposeList.Add(init);
if (init.Handle != Runtime.PyNone)
{
// if __init__ hasn't been overridden then it will be a managed object
ManagedType managedMethod = ManagedType.GetManagedObject(init.Handle);
if (null == managedMethod)
{
var pyargs = new PyObject[args.Length];
for (var i = 0; i < args.Length; ++i)
{
pyargs[i] = new PyObject(Converter.ToPython(args[i], args[i]?.GetType()));
disposeList.Add(pyargs[i]);
}

disposeList.Add(init.Invoke(pyargs));
}
}
}
finally
{
Expand Down
28 changes: 14 additions & 14 deletions src/runtime/metatype.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.InteropServices;

namespace Python.Runtime
Expand Down Expand Up @@ -125,19 +125,6 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
}


public static IntPtr tp_alloc(IntPtr mt, int n)
{
IntPtr type = Runtime.PyType_GenericAlloc(mt, n);
return type;
}


public static void tp_free(IntPtr tp)
{
Runtime.PyObject_GC_Del(tp);
}


/// <summary>
/// Metatype __call__ implementation. This is needed to ensure correct
/// initialization (__init__ support), because the tp_call we inherit
Expand Down Expand Up @@ -188,6 +175,19 @@ public static IntPtr tp_call(IntPtr tp, IntPtr args, IntPtr kw)
}



public static IntPtr tp_alloc(IntPtr mt, int n)
{
IntPtr type = Runtime.PyType_GenericAlloc(mt, n);
return type;
}


public static void tp_free(IntPtr tp)
{
Runtime.PyObject_GC_Del(tp);
}

/// <summary>
/// Type __setattr__ implementation for reflected types. Note that this
/// is slightly different than the standard setattr implementation for
Expand Down
14 changes: 14 additions & 0 deletions src/tests/test_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,17 @@ def test_isinstance_check():
for x in b:
assert isinstance(x, System.Object)
assert isinstance(x, System.String)


def test_derived_constructor_only_called_once():
calls = []

class X(System.Object):
__namespace__ = "PyTest"

def __init__(self, *args):
calls.append(args)

x = X(1, 2)

assert calls == [(1, 2)]