Skip to content
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
350 changes: 349 additions & 1 deletion src/embed_tests/TestMethodBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def TestEnumerable(self):
public static dynamic Numpy;

[OneTimeSetUp]
public void SetUp()
public void OneTimeSetUp()
{
PythonEngine.Initialize();
using var _ = Py.GIL();
Expand All @@ -89,6 +89,15 @@ public void Dispose()
PythonEngine.Shutdown();
}

[SetUp]
public void SetUp()
{
CSharpModel.LastDelegateCalled = null;
CSharpModel.LastFuncCalled = null;
CSharpModel.MethodCalled = null;
CSharpModel.ProvidedArgument = null;
}

[Test]
public void MethodCalledList()
{
Expand Down Expand Up @@ -1152,6 +1161,247 @@ def call_method():
Assert.AreEqual("MethodWithEnumParam With Enum", result.As<string>());
}

[TestCase("call_method_with_func1", "MethodWithFunc1", "func1")]
[TestCase("call_method_with_func2", "MethodWithFunc2", "func2")]
[TestCase("call_method_with_func3", "MethodWithFunc3", "func3")]
[TestCase("call_method_with_func1_lambda", "MethodWithFunc1", "func1")]
[TestCase("call_method_with_func2_lambda", "MethodWithFunc2", "func2")]
[TestCase("call_method_with_func3_lambda", "MethodWithFunc3", "func3")]
public void BindsPythonToCSharpFuncDelegates(string pythonFuncToCall, string expectedCSharpMethodCalled, string expectedPythonFuncCalled)
{
using var _ = Py.GIL();

var module = PyModule.FromString("BindsPythonToCSharpFuncDelegates", @$"
from clr import AddReference
AddReference(""System"")
from Python.EmbeddingTest import *

from System import Func

class PythonModel:
last_delegate_called = None

def func1():
PythonModel.last_delegate_called = 'func1'
return TestMethodBinder.CSharpModel();

def func2(model):
if model is None or not isinstance(model, TestMethodBinder.CSharpModel):
raise TypeError(""model must be of type CSharpModel"")
PythonModel.last_delegate_called = 'func2'
return model

def func3(model1, model2):
if model1 is None or model2 is None or not isinstance(model1, TestMethodBinder.CSharpModel) or not isinstance(model2, TestMethodBinder.CSharpModel):
raise TypeError(""model1 and model2 must be of type CSharpModel"")
PythonModel.last_delegate_called = 'func3'
return model1

def call_method_with_func1():
return TestMethodBinder.CSharpModel.MethodWithFunc1(func1)

def call_method_with_func2():
return TestMethodBinder.CSharpModel.MethodWithFunc2(func2)

def call_method_with_func3():
return TestMethodBinder.CSharpModel.MethodWithFunc3(func3)

def call_method_with_func1_lambda():
return TestMethodBinder.CSharpModel.MethodWithFunc1(lambda: func1())

def call_method_with_func2_lambda():
return TestMethodBinder.CSharpModel.MethodWithFunc2(lambda model: func2(model))

def call_method_with_func3_lambda():
return TestMethodBinder.CSharpModel.MethodWithFunc3(lambda model1, model2: func3(model1, model2))
");

CSharpModel managedResult = null;
Assert.DoesNotThrow(() =>
{
using var result = module.GetAttr(pythonFuncToCall).Invoke();
managedResult = result.As<CSharpModel>();
});

Assert.IsNotNull(managedResult);
Assert.AreEqual(expectedCSharpMethodCalled, CSharpModel.LastDelegateCalled);

using var pythonModel = module.GetAttr("PythonModel");
using var lastDelegateCalled = pythonModel.GetAttr("last_delegate_called");
Assert.AreEqual(expectedPythonFuncCalled, lastDelegateCalled.As<string>());
}

[TestCase("call_method_with_action1", "MethodWithAction1", "action1")]
[TestCase("call_method_with_action2", "MethodWithAction2", "action2")]
[TestCase("call_method_with_action3", "MethodWithAction3", "action3")]
[TestCase("call_method_with_action1_lambda", "MethodWithAction1", "action1")]
[TestCase("call_method_with_action2_lambda", "MethodWithAction2", "action2")]
[TestCase("call_method_with_action3_lambda", "MethodWithAction3", "action3")]
public void BindsPythonToCSharpActionDelegates(string pythonFuncToCall, string expectedCSharpMethodCalled, string expectedPythonFuncCalled)
{
using var _ = Py.GIL();

var module = PyModule.FromString("BindsPythonToCSharpActionDelegates", @$"
from clr import AddReference
AddReference(""System"")
from Python.EmbeddingTest import *

from System import Func

class PythonModel:
last_delegate_called = None

def action1():
PythonModel.last_delegate_called = 'action1'
pass

def action2(model):
if model is None or not isinstance(model, TestMethodBinder.CSharpModel):
raise TypeError(""model must be of type CSharpModel"")
PythonModel.last_delegate_called = 'action2'
pass

def action3(model1, model2):
if model1 is None or model2 is None or not isinstance(model1, TestMethodBinder.CSharpModel) or not isinstance(model2, TestMethodBinder.CSharpModel):
raise TypeError(""model1 and model2 must be of type CSharpModel"")
PythonModel.last_delegate_called = 'action3'
pass

def call_method_with_action1():
return TestMethodBinder.CSharpModel.MethodWithAction1(action1)

def call_method_with_action2():
return TestMethodBinder.CSharpModel.MethodWithAction2(action2)

def call_method_with_action3():
return TestMethodBinder.CSharpModel.MethodWithAction3(action3)

def call_method_with_action1_lambda():
return TestMethodBinder.CSharpModel.MethodWithAction1(lambda: action1())

def call_method_with_action2_lambda():
return TestMethodBinder.CSharpModel.MethodWithAction2(lambda model: action2(model))

def call_method_with_action3_lambda():
return TestMethodBinder.CSharpModel.MethodWithAction3(lambda model1, model2: action3(model1, model2))
");

Assert.DoesNotThrow(() =>
{
using var result = module.GetAttr(pythonFuncToCall).Invoke();
});

Assert.AreEqual(expectedCSharpMethodCalled, CSharpModel.LastDelegateCalled);

using var pythonModel = module.GetAttr("PythonModel");
using var lastDelegateCalled = pythonModel.GetAttr("last_delegate_called");
Assert.AreEqual(expectedPythonFuncCalled, lastDelegateCalled.As<string>());
}

[TestCase("call_method_with_func1", "MethodWithFunc1", "TestFunc1")]
[TestCase("call_method_with_func2", "MethodWithFunc2", "TestFunc2")]
[TestCase("call_method_with_func3", "MethodWithFunc3", "TestFunc3")]
public void BindsCSharpFuncFromPythonToCSharpFuncDelegates(string pythonFuncToCall, string expectedMethodCalled, string expectedInnerMethodCalled)
{
using var _ = Py.GIL();

var module = PyModule.FromString("BindsCSharpFuncFromPythonToCSharpFuncDelegates", @$"
from clr import AddReference
AddReference(""System"")
from Python.EmbeddingTest import *

def call_method_with_func1():
return TestMethodBinder.CSharpModel.MethodWithFunc1(TestMethodBinder.CSharpModel.TestFunc1)

def call_method_with_func2():
return TestMethodBinder.CSharpModel.MethodWithFunc2(TestMethodBinder.CSharpModel.TestFunc2)

def call_method_with_func3():
return TestMethodBinder.CSharpModel.MethodWithFunc3(TestMethodBinder.CSharpModel.TestFunc3)
");

CSharpModel managedResult = null;
Assert.DoesNotThrow(() =>
{
using var result = module.GetAttr(pythonFuncToCall).Invoke();
managedResult = result.As<CSharpModel>();
});
Assert.IsNotNull(managedResult);
Assert.AreEqual(expectedMethodCalled, CSharpModel.LastDelegateCalled);
Assert.AreEqual(expectedInnerMethodCalled, CSharpModel.LastFuncCalled);
}

[TestCase("call_method_with_action1", "MethodWithAction1", "TestAction1")]
[TestCase("call_method_with_action2", "MethodWithAction2", "TestAction2")]
[TestCase("call_method_with_action3", "MethodWithAction3", "TestAction3")]
public void BindsCSharpActionFromPythonToCSharpActionDelegates(string pythonFuncToCall, string expectedMethodCalled, string expectedInnerMethodCalled)
{
using var _ = Py.GIL();

var module = PyModule.FromString("BindsCSharpActionFromPythonToCSharpActionDelegates", @$"
from clr import AddReference
AddReference(""System"")
from Python.EmbeddingTest import *

def call_method_with_action1():
return TestMethodBinder.CSharpModel.MethodWithAction1(TestMethodBinder.CSharpModel.TestAction1)

def call_method_with_action2():
return TestMethodBinder.CSharpModel.MethodWithAction2(TestMethodBinder.CSharpModel.TestAction2)

def call_method_with_action3():
return TestMethodBinder.CSharpModel.MethodWithAction3(TestMethodBinder.CSharpModel.TestAction3)
");

Assert.DoesNotThrow(() =>
{
using var result = module.GetAttr(pythonFuncToCall).Invoke();
});
Assert.AreEqual(expectedMethodCalled, CSharpModel.LastDelegateCalled);
Assert.AreEqual(expectedInnerMethodCalled, CSharpModel.LastFuncCalled);
}

[Test]
public void NumericArgumentsTakePrecedenceOverEnums()
{
using var _ = Py.GIL();

var module = PyModule.FromString("NumericArgumentsTakePrecedenceOverEnums", @$"
from clr import AddReference
AddReference(""System"")
from Python.EmbeddingTest import *
from System import DayOfWeek

def call_method_with_int():
TestMethodBinder.CSharpModel().NumericalArgumentMethod(1)

def call_method_with_float():
TestMethodBinder.CSharpModel().NumericalArgumentMethod(0.1)

def call_method_with_numpy_float():
TestMethodBinder.CSharpModel().NumericalArgumentMethod(TestMethodBinder.Numpy.float64(0.1))

def call_method_with_enum():
TestMethodBinder.CSharpModel().NumericalArgumentMethod(DayOfWeek.MONDAY)
");

module.GetAttr("call_method_with_int").Invoke();
Assert.AreEqual(typeof(int), CSharpModel.ProvidedArgument.GetType());
Assert.AreEqual(1, CSharpModel.ProvidedArgument);

module.GetAttr("call_method_with_float").Invoke();
Assert.AreEqual(typeof(double), CSharpModel.ProvidedArgument.GetType());
Assert.AreEqual(0.1d, CSharpModel.ProvidedArgument);

module.GetAttr("call_method_with_numpy_float").Invoke();
Assert.AreEqual(typeof(decimal), CSharpModel.ProvidedArgument.GetType());
Assert.AreEqual(0.1m, CSharpModel.ProvidedArgument);

module.GetAttr("call_method_with_enum").Invoke();
Assert.AreEqual(typeof(DayOfWeek), CSharpModel.ProvidedArgument.GetType());
Assert.AreEqual(DayOfWeek.Monday, CSharpModel.ProvidedArgument);
}

// Used to test that we match this function with Py DateTime & Date Objects
public static int GetMonth(DateTime test)
{
Expand Down Expand Up @@ -1234,6 +1484,10 @@ public void NumericalArgumentMethod(decimal value)
{
ProvidedArgument = value;
}
public void NumericalArgumentMethod(DayOfWeek value)
{
ProvidedArgument = value;
}
public void EnumerableKeyValuePair(IEnumerable<KeyValuePair<string, decimal>> value)
{
ProvidedArgument = value;
Expand Down Expand Up @@ -1288,6 +1542,100 @@ public static void MethodDateTimeAndTimeSpan(CSharpModel pepe, Func<DateTime, Da
{
AssertErrorNotOccurred();
}

public static string LastDelegateCalled { get; set; }
public static string LastFuncCalled { get; set; }

public static CSharpModel MethodWithFunc1(Func<CSharpModel> func)
{
AssertErrorNotOccurred();
LastDelegateCalled = "MethodWithFunc1";
return func();
}

public static CSharpModel MethodWithFunc2(Func<CSharpModel, CSharpModel> func)
{
AssertErrorNotOccurred();
LastDelegateCalled = "MethodWithFunc2";
return func(new CSharpModel());
}

public static CSharpModel MethodWithFunc3(Func<CSharpModel, CSharpModel, CSharpModel> func)
{
AssertErrorNotOccurred();
LastDelegateCalled = "MethodWithFunc3";
return func(new CSharpModel(), new CSharpModel());
}

public static void MethodWithAction1(Action action)
{
AssertErrorNotOccurred();
LastDelegateCalled = "MethodWithAction1";
action();
}

public static void MethodWithAction2(Action<CSharpModel> action)
{
AssertErrorNotOccurred();
LastDelegateCalled = "MethodWithAction2";
action(new CSharpModel());
}

public static void MethodWithAction3(Action<CSharpModel, CSharpModel> action)
{
AssertErrorNotOccurred();
LastDelegateCalled = "MethodWithAction3";
action(new CSharpModel(), new CSharpModel());
}

public static CSharpModel TestFunc1()
{
LastFuncCalled = "TestFunc1";
return new CSharpModel();
}

public static CSharpModel TestFunc2(CSharpModel model)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
LastFuncCalled = "TestFunc2";
return model;
}

public static CSharpModel TestFunc3(CSharpModel model1, CSharpModel model2)
{
if (model1 == null || model2 == null)
{
throw new ArgumentNullException(model1 == null ? nameof(model1) : nameof(model2));
}
LastFuncCalled = "TestFunc3";
return model1;
}

public static void TestAction1()
{
LastFuncCalled = "TestAction1";
}

public static void TestAction2(CSharpModel model)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
LastFuncCalled = "TestAction2";
}

public static void TestAction3(CSharpModel model1, CSharpModel model2)
{
if (model1 == null || model2 == null)
{
throw new ArgumentNullException(model1 == null ? nameof(model1) : nameof(model2));
}
LastFuncCalled = "TestAction3";
}
}

public class TestImplicitConversion
Expand Down
Loading
Loading