From 7daa47f9a3582eb59fecadda190dd814f4250a24 Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Mon, 28 Jan 2019 11:53:25 -0800 Subject: [PATCH 1/5] added a regression test for #881 https://github.com/pythonnet/pythonnet/issues/811 --- src/embed_tests/Python.EmbeddingTest.csproj | 1 + src/embed_tests/TestInstanceWrapping.cs | 58 +++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/embed_tests/TestInstanceWrapping.cs diff --git a/src/embed_tests/Python.EmbeddingTest.csproj b/src/embed_tests/Python.EmbeddingTest.csproj index 7a0964a8a..1c8157038 100644 --- a/src/embed_tests/Python.EmbeddingTest.csproj +++ b/src/embed_tests/Python.EmbeddingTest.csproj @@ -93,6 +93,7 @@ + diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs new file mode 100644 index 000000000..ec275d67a --- /dev/null +++ b/src/embed_tests/TestInstanceWrapping.cs @@ -0,0 +1,58 @@ +using System; +using System.Globalization; +using NUnit.Framework; +using Python.Runtime; + +namespace Python.EmbeddingTest +{ + public class TestInstanceWrapping + { + [OneTimeSetUp] + public void SetUp() + { + PythonEngine.Initialize(); + } + + [OneTimeTearDown] + public void Dispose() + { + PythonEngine.Shutdown(); + } + + // regression test for https://github.com/pythonnet/pythonnet/issues/811 + [Test] + public void OverloadResolution_UnknownToObject() + { + var overloaded = new Overloaded(); + using (Py.GIL()) + { + var o = overloaded.ToPython(); + + dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(KeyError())"); + callWithSelf(o); + Assert.AreEqual(Overloaded.Object, overloaded.Value); + } + } + + class Base {} + class Derived: Base { } + + class Overloaded: Derived + { + public int Value { get; set; } + public void IntOrStr(int arg) => this.Value = arg; + public void IntOrStr(string arg) => + this.Value = int.Parse(arg, NumberStyles.Integer, CultureInfo.InvariantCulture); + + public const int Object = 1; + public const int ConcreteClass = 2; + public void ObjOrClass(object _) => this.Value = Object; + public void ObjOrClass(Overloaded _) => this.Value = ConcreteClass; + + public const int Base = ConcreteClass + 1; + public const int Derived = Base + 1; + public void BaseOrDerived(Base _) => this.Value = Base; + public void BaseOrDerived(Derived _) => this.Value = Derived; + } + } +} From ad360311c05e2b750ea53631b46ddae3ee82f5c8 Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Sun, 10 Feb 2019 22:38:50 -0800 Subject: [PATCH 2/5] when converting to object, wrap values of unknown type into PyObject instead of failing This enables overload resolution with object parameters to behave the same way PyObject parameters behave - e.g. allow any Python object to be passed as PyObject as fallback. Resolves #811 --- CHANGELOG.md | 1 + src/runtime/converter.cs | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82fccbe35..5bc0c9981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. - Reattach python exception traceback information (#545) - PythonEngine.Intialize will now call `Py_InitializeEx` with a default value of 0, so signals will not be configured by default on embedding. This is different from the previous behaviour, where `Py_Initialize` was called instead, which sets initSigs to 1. ([#449][i449]) - Refactored MethodBinder.Bind in preparation to make it extensible (#829) +- When calling C# from Python, enable passing argument of any type to a parameter of C# type `object` by wrapping it into `PyObject` instance. ([#881][i881]) - Look for installed Windows 10 sdk's during installation instead of relying on specific versions. ### Fixed diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 7c53bdcb1..3add8aba0 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -398,12 +398,9 @@ internal static bool ToManagedValue(IntPtr value, Type obType, return ToArray(value, typeof(object[]), out result, setError); } - if (setError) - { - Exceptions.SetError(Exceptions.TypeError, "value cannot be converted to Object"); - } - - return false; + Runtime.XIncref(value); // PyObject() assumes ownership + result = new PyObject(value); + return true; } // Conversion to 'Type' is done using the same mappings as above for objects. From 0e4b3ad8892f845acdfb692e22ce782396230bd7 Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Thu, 20 Jun 2019 12:19:05 -0700 Subject: [PATCH 3/5] fixed ObjectField conversion test --- src/tests/test_conversion.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tests/test_conversion.py b/src/tests/test_conversion.py index 0ba10a80e..e61eda26c 100644 --- a/src/tests/test_conversion.py +++ b/src/tests/test_conversion.py @@ -595,11 +595,10 @@ def test_object_conversion(): # need to test subclass here - with pytest.raises(TypeError): - class Foo(object): - pass - ob = ConversionTest() - ob.ObjectField = Foo + class Foo(object): + pass + ob.ObjectField = Foo + assert ob.ObjectField == Foo def test_enum_conversion(): From b948f580e89be78a681a8f185849817a354c709c Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Wed, 26 Jun 2019 22:40:43 -0700 Subject: [PATCH 4/5] fixed test_object_indexer to pass on custom class key --- src/tests/test_indexer.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/test_indexer.py b/src/tests/test_indexer.py index 6f18550d9..ca4fd3b89 100644 --- a/src/tests/test_indexer.py +++ b/src/tests/test_indexer.py @@ -438,13 +438,13 @@ def test_object_indexer(): ob[long(1)] = "long" assert ob[long(1)] == "long" - with pytest.raises(TypeError): - class Eggs(object): - pass + class Eggs(object): + pass - key = Eggs() - ob = Test.ObjectIndexerTest() - ob[key] = "wrong" + key = Eggs() + ob = Test.ObjectIndexerTest() + ob[key] = "eggs_key" + assert ob[key] == "eggs_key" def test_interface_indexer(): From 6373197ac5f1dd9a44f2c85840fd21a9b3ef62a6 Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Wed, 26 Feb 2020 17:49:10 -0800 Subject: [PATCH 5/5] use object() instance in OverloadResolution_UnknownToObject test --- src/embed_tests/TestInstanceWrapping.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/embed_tests/TestInstanceWrapping.cs b/src/embed_tests/TestInstanceWrapping.cs index ec275d67a..8be207c00 100644 --- a/src/embed_tests/TestInstanceWrapping.cs +++ b/src/embed_tests/TestInstanceWrapping.cs @@ -28,7 +28,7 @@ public void OverloadResolution_UnknownToObject() { var o = overloaded.ToPython(); - dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(KeyError())"); + dynamic callWithSelf = PythonEngine.Eval("lambda o: o.ObjOrClass(object())"); callWithSelf(o); Assert.AreEqual(Overloaded.Object, overloaded.Value); }