diff --git a/CHANGELOG.md b/CHANGELOG.md index 876bff07d..f8b0aed48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ Python `float` will continue to be converted to `System.Double`. - BREAKING: `PyObject.GetAttr(name, default)` now only ignores `AttributeError` (previously ignored all exceptions). - BREAKING: `PyObject` no longer implements `IEnumerable`. Instead, `PyIterable` does that. +- BREAKING: `IPyObjectDecoder.CanDecode` `objectType` parameter type changed from `PyObject` to `PyType` ### Fixed diff --git a/src/embed_tests/Codecs.cs b/src/embed_tests/Codecs.cs index 1169bca34..1beddbec9 100644 --- a/src/embed_tests/Codecs.cs +++ b/src/embed_tests/Codecs.cs @@ -141,16 +141,17 @@ public void SequenceDecoderTest() //SequenceConverter can only convert to any ICollection var pyList = new PyList(items.ToArray()); + var listType = pyList.GetPythonType(); //it can convert a PyList, since PyList satisfies the python sequence protocol - Assert.IsFalse(codec.CanDecode(pyList, typeof(bool))); - Assert.IsFalse(codec.CanDecode(pyList, typeof(IList))); - Assert.IsFalse(codec.CanDecode(pyList, typeof(System.Collections.IEnumerable))); - Assert.IsFalse(codec.CanDecode(pyList, typeof(IEnumerable))); + Assert.IsFalse(codec.CanDecode(listType, typeof(bool))); + Assert.IsFalse(codec.CanDecode(listType, typeof(IList))); + Assert.IsFalse(codec.CanDecode(listType, typeof(System.Collections.IEnumerable))); + Assert.IsFalse(codec.CanDecode(listType, typeof(IEnumerable))); - Assert.IsTrue(codec.CanDecode(pyList, typeof(ICollection))); - Assert.IsTrue(codec.CanDecode(pyList, typeof(ICollection))); - Assert.IsTrue(codec.CanDecode(pyList, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); + Assert.IsTrue(codec.CanDecode(listType, typeof(ICollection))); //convert to collection of int ICollection intCollection = null; @@ -380,7 +381,7 @@ public void As_Object_AffectedByDecoders() public class EverythingElseToSelfDecoder : IPyObjectDecoder { - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) { return targetType.IsAssignableFrom(typeof(EverythingElseToSelfDecoder)); } @@ -399,7 +400,7 @@ public ValueErrorWrapper(string message) : base(message) { } class ValueErrorCodec : IPyObjectEncoder, IPyObjectDecoder { - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) => this.CanEncode(targetType) && PythonReferenceComparer.Instance.Equals(objectType, PythonEngine.Eval("ValueError")); @@ -424,7 +425,7 @@ class InstancelessExceptionDecoder : IPyObjectDecoder { readonly PyObject PyErr = Py.Import("clr.interop").GetAttr("PyErr"); - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) => PythonReferenceComparer.Instance.Equals(PyErr, objectType); public bool TryDecode(PyObject pyObj, out T value) @@ -466,7 +467,7 @@ public DecoderReturningPredefinedValue(PyObject objectType, TTarget decodeResult this.DecodeResult = decodeResult; } - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) => objectType.Handle == TheOnlySupportedSourceType.Handle && targetType == typeof(TTarget); public bool TryDecode(PyObject pyObj, out T value) @@ -485,7 +486,7 @@ public static void Setup() PyObjectConversions.RegisterDecoder(new DateTimeDecoder()); } - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) { return targetType == typeof(DateTime); } diff --git a/src/runtime/Codecs/DecoderGroup.cs b/src/runtime/Codecs/DecoderGroup.cs index cc511ed50..b72cd796c 100644 --- a/src/runtime/Codecs/DecoderGroup.cs +++ b/src/runtime/Codecs/DecoderGroup.cs @@ -27,7 +27,7 @@ public void Add(IPyObjectDecoder item) public void Clear() => this.decoders.Clear(); /// - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) => this.decoders.Any(decoder => decoder.CanDecode(objectType, targetType)); /// public bool TryDecode(PyObject pyObj, out T value) @@ -58,7 +58,7 @@ public static class DecoderGroupExtensions /// public static IPyObjectDecoder GetDecoder( this IPyObjectDecoder decoder, - PyObject objectType, Type targetType) + PyType objectType, Type targetType) { if (decoder is null) throw new ArgumentNullException(nameof(decoder)); diff --git a/src/runtime/Codecs/EnumPyIntCodec.cs b/src/runtime/Codecs/EnumPyIntCodec.cs index 5e6c4c7d3..8e68837f3 100644 --- a/src/runtime/Codecs/EnumPyIntCodec.cs +++ b/src/runtime/Codecs/EnumPyIntCodec.cs @@ -7,7 +7,7 @@ public sealed class EnumPyIntCodec : IPyObjectEncoder, IPyObjectDecoder { public static EnumPyIntCodec Instance { get; } = new EnumPyIntCodec(); - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) { return targetType.IsEnum && objectType.IsSubclass(new BorrowedReference(Runtime.PyLongType)); diff --git a/src/runtime/Codecs/IterableDecoder.cs b/src/runtime/Codecs/IterableDecoder.cs index 346057238..bcc2eca01 100644 --- a/src/runtime/Codecs/IterableDecoder.cs +++ b/src/runtime/Codecs/IterableDecoder.cs @@ -17,12 +17,12 @@ internal static bool IsIterable(Type targetType) return targetType.GetGenericTypeDefinition() == typeof(IEnumerable<>); } - internal static bool IsIterable(PyObject objectType) + internal static bool IsIterable(PyType objectType) { return objectType.HasAttr("__iter__"); } - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) { return IsIterable(objectType) && IsIterable(targetType); } diff --git a/src/runtime/Codecs/ListDecoder.cs b/src/runtime/Codecs/ListDecoder.cs index 013f3f3f9..439c87df8 100644 --- a/src/runtime/Codecs/ListDecoder.cs +++ b/src/runtime/Codecs/ListDecoder.cs @@ -13,7 +13,7 @@ private static bool IsList(Type targetType) return targetType.GetGenericTypeDefinition() == typeof(IList<>); } - private static bool IsList(PyObject objectType) + private static bool IsList(PyType objectType) { //TODO accept any python object that implements the sequence and list protocols //must implement sequence protocol to fully implement list protocol @@ -23,7 +23,7 @@ private static bool IsList(PyObject objectType) return objectType.Handle == Runtime.PyListType; } - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) { return IsList(objectType) && IsList(targetType); } diff --git a/src/runtime/Codecs/SequenceDecoder.cs b/src/runtime/Codecs/SequenceDecoder.cs index dce08fd99..a539297cd 100644 --- a/src/runtime/Codecs/SequenceDecoder.cs +++ b/src/runtime/Codecs/SequenceDecoder.cs @@ -13,7 +13,7 @@ internal static bool IsSequence(Type targetType) return targetType.GetGenericTypeDefinition() == typeof(ICollection<>); } - internal static bool IsSequence(PyObject objectType) + internal static bool IsSequence(PyType objectType) { //must implement iterable protocol to fully implement sequence protocol if (!IterableDecoder.IsIterable(objectType)) return false; @@ -25,7 +25,7 @@ internal static bool IsSequence(PyObject objectType) return objectType.HasAttr("__getitem__"); } - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) { return IsSequence(objectType) && IsSequence(targetType); } diff --git a/src/runtime/Codecs/TupleCodecs.cs b/src/runtime/Codecs/TupleCodecs.cs index 823c42489..cd4d519ba 100644 --- a/src/runtime/Codecs/TupleCodecs.cs +++ b/src/runtime/Codecs/TupleCodecs.cs @@ -41,7 +41,7 @@ public PyObject TryEncode(object value) return new PyTuple(StolenReference.DangerousFromPointer(tuple)); } - public bool CanDecode(PyObject objectType, Type targetType) + public bool CanDecode(PyType objectType, Type targetType) => objectType.Handle == Runtime.PyTupleType && this.CanEncode(targetType); public bool TryDecode(PyObject pyObj, out T value) diff --git a/src/runtime/converterextensions.cs b/src/runtime/converterextensions.cs index 5711b9f87..2396fb0bd 100644 --- a/src/runtime/converterextensions.cs +++ b/src/runtime/converterextensions.cs @@ -3,6 +3,7 @@ namespace Python.Runtime using System; using System.Collections.Concurrent; using System.Collections.Generic; + using System.Diagnostics; using System.Linq; using System.Reflection; using Python.Runtime.Codecs; @@ -15,7 +16,7 @@ public interface IPyObjectDecoder /// /// Checks if this decoder can decode from to /// - bool CanDecode(PyObject objectType, Type targetType); + bool CanDecode(PyType objectType, Type targetType); /// /// Attempts do decode into a variable of specified type /// @@ -124,7 +125,9 @@ internal static bool TryDecode(IntPtr pyHandle, IntPtr pyType, Type targetType, static Converter.TryConvertFromPythonDelegate GetDecoder(IntPtr sourceType, Type targetType) { IPyObjectDecoder decoder; - using (var pyType = new PyObject(Runtime.SelfIncRef(sourceType))) + var sourceTypeRef = new BorrowedReference(sourceType); + Debug.Assert(PyType.IsType(sourceTypeRef)); + using (var pyType = new PyType(sourceTypeRef, prevalidated: true)) { lock (decoders) {