From 213afb65ca73d3bd7e3eb9b60a6edf54cb0a36eb Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Thu, 23 Apr 2020 11:14:34 -0700 Subject: [PATCH 1/4] added RawProxyEncoder Now Python host can force raw encoding for autoconverted .NET types. Enables workaround for #514 --- src/embed_tests/CodecGroups.cs | 16 ++++++++-------- src/embed_tests/Codecs.cs | 4 ++-- src/runtime/Codecs/RawProxyEncoder.cs | 20 ++++++++++++++++++++ src/testing/conversiontest.cs | 5 ++++- src/tests/test_conversion.py | 17 +++++++++++++++++ 5 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 src/runtime/Codecs/RawProxyEncoder.cs diff --git a/src/embed_tests/CodecGroups.cs b/src/embed_tests/CodecGroups.cs index 68cf2d6e5..5dd40210f 100644 --- a/src/embed_tests/CodecGroups.cs +++ b/src/embed_tests/CodecGroups.cs @@ -11,10 +11,10 @@ public class CodecGroups [Test] public void GetEncodersByType() { - var encoder1 = new ObjectToRawProxyEncoder(); - var encoder2 = new ObjectToRawProxyEncoder(); + var encoder1 = new ObjectToEncoderInstanceEncoder(); + var encoder2 = new ObjectToEncoderInstanceEncoder(); var group = new EncoderGroup { - new ObjectToRawProxyEncoder>(), + new ObjectToEncoderInstanceEncoder>(), encoder1, encoder2, }; @@ -27,8 +27,8 @@ public void GetEncodersByType() public void CanEncode() { var group = new EncoderGroup { - new ObjectToRawProxyEncoder>(), - new ObjectToRawProxyEncoder(), + new ObjectToEncoderInstanceEncoder>(), + new ObjectToEncoderInstanceEncoder(), }; Assert.IsTrue(group.CanEncode(typeof(Tuple))); @@ -39,9 +39,9 @@ public void CanEncode() [Test] public void Encodes() { - var encoder0 = new ObjectToRawProxyEncoder>(); - var encoder1 = new ObjectToRawProxyEncoder(); - var encoder2 = new ObjectToRawProxyEncoder(); + var encoder0 = new ObjectToEncoderInstanceEncoder>(); + var encoder1 = new ObjectToEncoderInstanceEncoder(); + var encoder2 = new ObjectToEncoderInstanceEncoder(); var group = new EncoderGroup { encoder0, encoder1, diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index 0d15ca55f..d872dbd12 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -86,9 +86,9 @@ static void TupleRoundtripGeneric() { /// /// "Decodes" only objects of exact type . - /// Result is just a raw Python object proxy. + /// Result is just the raw proxy to the encoder instance itself. /// - class ObjectToRawProxyEncoder : IPyObjectEncoder + class ObjectToEncoderInstanceEncoder : IPyObjectEncoder { public bool CanEncode(Type type) => type == typeof(T); public PyObject TryEncode(object value) => this.GetRawPythonProxy(); diff --git a/src/runtime/Codecs/RawProxyEncoder.cs b/src/runtime/Codecs/RawProxyEncoder.cs new file mode 100644 index 000000000..9c1377332 --- /dev/null +++ b/src/runtime/Codecs/RawProxyEncoder.cs @@ -0,0 +1,20 @@ +using System; + +namespace Python.Runtime.Codecs +{ + /// + /// A .NET object encoder, that returns raw proxies (e.g. no conversion to Python types). + /// You must inherit from this class and override . + /// + [Obsolete(Util.UnstableApiMessage)] + public abstract class RawProxyEncoder: IPyObjectEncoder + { + public PyObject TryEncode(object value) + { + if (value is null) throw new ArgumentNullException(nameof(value)); + + return value.GetRawPythonProxy(); + } + public abstract bool CanEncode(Type type); + } +} diff --git a/src/testing/conversiontest.cs b/src/testing/conversiontest.cs index 06ab7cb4e..1f9d64e1b 100644 --- a/src/testing/conversiontest.cs +++ b/src/testing/conversiontest.cs @@ -1,7 +1,9 @@ namespace Python.Test { + using System.Collections.Generic; + /// - /// Supports units tests for field access. + /// Supports unit tests for field access. /// public class ConversionTest { @@ -32,6 +34,7 @@ public ConversionTest() public byte[] ByteArrayField; public sbyte[] SByteArrayField; + public readonly List ListField = new List(); public T? Echo(T? arg) where T: struct { return arg; diff --git a/src/tests/test_conversion.py b/src/tests/test_conversion.py index e61eda26c..b52266288 100644 --- a/src/tests/test_conversion.py +++ b/src/tests/test_conversion.py @@ -6,6 +6,8 @@ import System import pytest from Python.Test import ConversionTest, UnicodeString +from Python.Runtime import PyObjectConversions +from Python.Runtime.Codecs import RawProxyEncoder from ._compat import indexbytes, long, unichr, text_type, PY2, PY3 @@ -700,3 +702,18 @@ def test_sbyte_array_conversion(): array = ob.SByteArrayField for i, _ in enumerate(value): assert array[i] == indexbytes(value, i) + +def test_codecs(): + """Test codec registration from Python""" + class ListAsRawEncoder(RawProxyEncoder): + def CanEncode(self, clr_type): + return clr_type.Name == "List`1" and clr_type.Namespace == "System.Collections.Generic" + + list_raw_encoder = ListAsRawEncoder() + PyObjectConversions.RegisterEncoder(list_raw_encoder) + + ob = ConversionTest() + + l = ob.ListField + l.Add(42) + assert ob.ListField.Count == 1 From 97940aef6c9b381fab948d9947f8e738d21a1376 Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Thu, 23 Apr 2020 11:46:49 -0700 Subject: [PATCH 2/4] workaround for inability to derive from C# abstract classes --- src/runtime/Codecs/RawProxyEncoder.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/runtime/Codecs/RawProxyEncoder.cs b/src/runtime/Codecs/RawProxyEncoder.cs index 9c1377332..dd6f21ee0 100644 --- a/src/runtime/Codecs/RawProxyEncoder.cs +++ b/src/runtime/Codecs/RawProxyEncoder.cs @@ -7,7 +7,7 @@ namespace Python.Runtime.Codecs /// You must inherit from this class and override . /// [Obsolete(Util.UnstableApiMessage)] - public abstract class RawProxyEncoder: IPyObjectEncoder + public class RawProxyEncoder: IPyObjectEncoder { public PyObject TryEncode(object value) { @@ -15,6 +15,7 @@ public PyObject TryEncode(object value) return value.GetRawPythonProxy(); } - public abstract bool CanEncode(Type type); + + public virtual bool CanEncode(Type type) => false; } } From 25fe10274404dd8974e0855c7a43fb14c0888f18 Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Thu, 23 Apr 2020 12:04:14 -0700 Subject: [PATCH 3/4] fixed missing __namespace__ in C#-derived Python type --- src/tests/test_conversion.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/test_conversion.py b/src/tests/test_conversion.py index b52266288..1386a0358 100644 --- a/src/tests/test_conversion.py +++ b/src/tests/test_conversion.py @@ -706,6 +706,7 @@ def test_sbyte_array_conversion(): def test_codecs(): """Test codec registration from Python""" class ListAsRawEncoder(RawProxyEncoder): + __namespace__ = "Python.Test" def CanEncode(self, clr_type): return clr_type.Name == "List`1" and clr_type.Namespace == "System.Collections.Generic" From 407e6df426366b1e8304c1202c3af2af51e2b003 Mon Sep 17 00:00:00 2001 From: Victor Milovanov Date: Thu, 23 Apr 2020 12:53:43 -0700 Subject: [PATCH 4/4] added RawProxyEncoder to the old project file --- src/runtime/Python.Runtime.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 0a4359796..2e47805cb 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -78,6 +78,7 @@ +