Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Another shot at #495 #503

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

Merged
merged 10 commits into from
Jun 26, 2017
Merged
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
36 changes: 1 addition & 35 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 @@ -808,7 +808,6 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec
obj,
args);

var disposeList = new List<PyObject>();
CLRObject self = null;
IntPtr gs = Runtime.PyGILState_Ensure();
try
Expand All @@ -821,42 +820,9 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec
// object to be collected.
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
{
foreach (PyObject x in disposeList)
{
x?.Dispose();
}

// Decrement the python object's reference count.
// This doesn't actually destroy the object, it just sets the reference to this object
// to be a weak reference and it will be destroyed when the C# object is destroyed.
Expand Down
18 changes: 4 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 @@ -157,23 +157,13 @@ public static IntPtr tp_call(IntPtr tp, IntPtr args, IntPtr kw)
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__);
var init = Runtime.PyObject_GetAttrString(obj, "__init__");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are leaking this reference.

Copy link
Contributor Author

@rickardraysearch rickardraysearch Jun 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Should be fixed now.

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);
IntPtr result = Runtime.PyObject_Call(init, args, kw);
Runtime.XDecref(init);

if (result == IntPtr.Zero)
{
Expand Down
23 changes: 0 additions & 23 deletions src/runtime/runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,29 +378,6 @@ internal static void CheckExceptionOccurred()
}
}

internal static IntPtr GetBoundArgTuple(IntPtr obj, IntPtr args)
{
if (PyObject_TYPE(args) != PyTupleType)
{
Exceptions.SetError(Exceptions.TypeError, "tuple expected");
return IntPtr.Zero;
}
int size = PyTuple_Size(args);
IntPtr items = PyTuple_New(size + 1);
PyTuple_SetItem(items, 0, obj);
XIncref(obj);

for (var i = 0; i < size; i++)
{
IntPtr item = PyTuple_GetItem(args, i);
XIncref(item);
PyTuple_SetItem(items, i + 1, item);
}

return items;
}


internal static IntPtr ExtendTuple(IntPtr t, params IntPtr[] args)
{
int size = PyTuple_Size(t);
Expand Down
82 changes: 68 additions & 14 deletions src/tests/test_subclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
from ._compat import range


def interface_test_class_fixture():
def interface_test_class_fixture(subnamespace):
"""Delay creation of class until test starts."""

class InterfaceTestClass(IInterfaceTest):
"""class that implements the test interface"""
__namespace__ = "Python.Test"
__namespace__ = "Python.Test." + subnamespace

def foo(self):
return "InterfaceTestClass"
Expand All @@ -31,12 +31,12 @@ def bar(self, x, i):
return InterfaceTestClass


def derived_class_fixture():
def derived_class_fixture(subnamespace):
"""Delay creation of class until test starts."""

class DerivedClass(SubClassTest):
"""class that derives from a class deriving from IInterfaceTest"""
__namespace__ = "Python.Test"
__namespace__ = "Python.Test." + subnamespace

def foo(self):
return "DerivedClass"
Expand All @@ -60,12 +60,12 @@ def return_list(self):
return DerivedClass


def derived_event_test_class_fixture():
def derived_event_test_class_fixture(subnamespace):
"""Delay creation of class until test starts."""

class DerivedEventTest(IInterfaceTest):
"""class that implements IInterfaceTest.TestEvent"""
__namespace__ = "Python.Test"
__namespace__ = "Python.Test." + subnamespace

def __init__(self):
self.event_handlers = []
Expand Down Expand Up @@ -99,7 +99,7 @@ def test_base_class():

def test_interface():
"""Test python classes can derive from C# interfaces"""
InterfaceTestClass = interface_test_class_fixture()
InterfaceTestClass = interface_test_class_fixture(test_interface.__name__)
ob = InterfaceTestClass()
assert ob.foo() == "InterfaceTestClass"
assert FunctionsTest.test_foo(ob) == "InterfaceTestClass"
Expand All @@ -112,7 +112,7 @@ def test_interface():

def test_derived_class():
"""Test python class derived from managed type"""
DerivedClass = derived_class_fixture()
DerivedClass = derived_class_fixture(test_derived_class.__name__)
ob = DerivedClass()
assert ob.foo() == "DerivedClass"
assert ob.base_foo() == "foo"
Expand All @@ -128,10 +128,9 @@ def test_derived_class():
assert id(x) == id(ob)


@pytest.mark.skip(reason="FIXME: test randomly pass/fails")
def test_create_instance():
"""Test derived instances can be created from managed code"""
DerivedClass = derived_class_fixture()
DerivedClass = derived_class_fixture(test_create_instance.__name__)
ob = FunctionsTest.create_instance(DerivedClass)
assert ob.foo() == "DerivedClass"
assert FunctionsTest.test_foo(ob) == "DerivedClass"
Expand All @@ -142,7 +141,7 @@ def test_create_instance():
x = FunctionsTest.pass_through(ob)
assert id(x) == id(ob)

InterfaceTestClass = interface_test_class_fixture()
InterfaceTestClass = interface_test_class_fixture(test_create_instance.__name__)
ob2 = FunctionsTest.create_instance(InterfaceTestClass)
assert ob2.foo() == "InterfaceTestClass"
assert FunctionsTest.test_foo(ob2) == "InterfaceTestClass"
Expand All @@ -153,7 +152,6 @@ def test_create_instance():
assert id(y) == id(ob2)


@pytest.mark.skip(reason="FIXME: test randomly pass/fails")
def test_events():
class EventHandler(object):
def handler(self, x, args):
Expand All @@ -166,12 +164,12 @@ def handler(self, x, args):
assert FunctionsTest.test_event(x, 1) == 1
assert event_handler.value == 1

InterfaceTestClass = interface_test_class_fixture()
InterfaceTestClass = interface_test_class_fixture(test_events.__name__)
i = InterfaceTestClass()
with pytest.raises(System.NotImplementedException):
FunctionsTest.test_event(i, 2)

DerivedEventTest = derived_event_test_class_fixture()
DerivedEventTest = derived_event_test_class_fixture(test_events.__name__)
d = DerivedEventTest()
d.add_TestEvent(event_handler.handler)
assert FunctionsTest.test_event(d, 3) == 3
Expand All @@ -190,3 +188,59 @@ def test_isinstance_check():
for x in b:
assert isinstance(x, System.Object)
assert isinstance(x, System.String)

def test_namespace_and_init():
calls = []
class TestX(System.Object):
__namespace__ = "test_clr_subclass_with_init_args"
def __init__(self, *args, **kwargs):
calls.append((args, kwargs))
t = TestX(1,2,3,foo="bar")
assert len(calls) == 1
assert calls[0][0] == (1,2,3)
assert calls[0][1] == {"foo":"bar"}

def test_namespace_and_argless_init():
calls = []
class TestX(System.Object):
__namespace__ = "test_clr_subclass_without_init_args"
def __init__(self):
calls.append(True)
t = TestX()
assert len(calls) == 1
assert calls[0] == True


def test_namespace_and_no_init():
class TestX(System.Object):
__namespace__ = "test_clr_subclass_without_init"
q = 1
t = TestX()
assert t.q == 1

def test_construction_from_clr():
import clr
calls = []
class TestX(System.Object):
__namespace__ = "test_clr_subclass_init_from_clr"
@clr.clrmethod(None, [int, str])
def __init__(self, i, s):
calls.append((i, s))

# Construct a TestX from Python
t = TestX(1, "foo")
assert len(calls) == 1
assert calls[0][0] == 1
assert calls[0][1] == "foo"

# Reset calls and construct a TestX from CLR
calls = []
tp = t.GetType()
t2 = tp.GetConstructors()[0].Invoke(None)
assert len(calls) == 0

# The object has only been constructed, now it needs to be initialized as well
tp.GetMethod("__init__").Invoke(t2, [1, "foo"])
assert len(calls) == 1
assert calls[0][0] == 1
assert calls[0][1] == "foo"