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 1 commit
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
Next Next commit
Drop tp_call implementation from metatype.
This seems to be a patch for broken behaviour in Python. Removing it
should solve #495 (`__init__` called twice on construction).
  • Loading branch information
filmor committed Jun 16, 2017
commit 9f64f5637e433b5d946051e2f8780c48dbbf36bf
53 changes: 1 addition & 52 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 @@ -137,57 +137,6 @@ 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
/// from PyType_Type won't call __init__ for metatypes it doesn't know.
/// </summary>
public static IntPtr tp_call(IntPtr tp, IntPtr args, IntPtr kw)
{
IntPtr func = Marshal.ReadIntPtr(tp, TypeOffset.tp_new);
if (func == IntPtr.Zero)
{
return Exceptions.RaiseTypeError("invalid object");
}

IntPtr obj = NativeCall.Call_3(func, tp, args, kw);
if (obj == IntPtr.Zero)
{
return IntPtr.Zero;
}

IntPtr py__init__ = Runtime.PyString_FromString("__init__");
IntPtr type = Runtime.PyObject_TYPE(obj);
IntPtr init = Runtime._PyType_Lookup(type, py__init__);
Runtime.XDecref(py__init__);
Runtime.PyErr_Clear();

if (init != IntPtr.Zero)
{
IntPtr bound = Runtime.GetBoundArgTuple(obj, args);
if (bound == IntPtr.Zero)
{
Runtime.XDecref(obj);
return IntPtr.Zero;
}

IntPtr result = Runtime.PyObject_Call(init, bound, kw);
Runtime.XDecref(bound);

if (result == IntPtr.Zero)
{
Runtime.XDecref(obj);
return IntPtr.Zero;
}

Runtime.XDecref(result);
}

return obj;
}


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


_derived_cons_calls = 0

def test_derived_constructor_only_called_once():
global _derived_cons_calls
_derived_cons_calls = 0

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

def __init__(self):
global _derived_cons_calls
_derived_cons_calls += 1

x = X()

assert _derived_cons_calls == 1