| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,132 @@ | |||
| 1 | + namespace Python.EmbeddingTest | ||
| 2 | + { | ||
| 3 | + using System; | ||
| 4 | + using System.Linq; | ||
| 5 | + using NUnit.Framework; | ||
| 6 | + using Python.Runtime; | ||
| 7 | + using Python.Runtime.Codecs; | ||
| 8 | + | ||
| 9 | + public class CodecGroups | ||
| 10 | + { | ||
| 11 | + [Test] | ||
| 12 | + public void GetEncodersByType() | ||
| 13 | + { | ||
| 14 | + var encoder1 = new ObjectToRawProxyEncoder<Uri>(); | ||
| 15 | + var encoder2 = new ObjectToRawProxyEncoder<Uri>(); | ||
| 16 | + var group = new EncoderGroup { | ||
| 17 | + new ObjectToRawProxyEncoder<Tuple<int>>(), | ||
| 18 | + encoder1, | ||
| 19 | + encoder2, | ||
| 20 | + }; | ||
| 21 | + | ||
| 22 | + var got = group.GetEncoders(typeof(Uri)).ToArray(); | ||
| 23 | + CollectionAssert.AreEqual(new[]{encoder1, encoder2}, got); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + [Test] | ||
| 27 | + public void CanEncode() | ||
| 28 | + { | ||
| 29 | + var group = new EncoderGroup { | ||
| 30 | + new ObjectToRawProxyEncoder<Tuple<int>>(), | ||
| 31 | + new ObjectToRawProxyEncoder<Uri>(), | ||
| 32 | + }; | ||
| 33 | + | ||
| 34 | + Assert.IsTrue(group.CanEncode(typeof(Tuple<int>))); | ||
| 35 | + Assert.IsTrue(group.CanEncode(typeof(Uri))); | ||
| 36 | + Assert.IsFalse(group.CanEncode(typeof(string))); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + [Test] | ||
| 40 | + public void Encodes() | ||
| 41 | + { | ||
| 42 | + var encoder0 = new ObjectToRawProxyEncoder<Tuple<int>>(); | ||
| 43 | + var encoder1 = new ObjectToRawProxyEncoder<Uri>(); | ||
| 44 | + var encoder2 = new ObjectToRawProxyEncoder<Uri>(); | ||
| 45 | + var group = new EncoderGroup { | ||
| 46 | + encoder0, | ||
| 47 | + encoder1, | ||
| 48 | + encoder2, | ||
| 49 | + }; | ||
| 50 | + | ||
| 51 | + var uri = group.TryEncode(new Uri("data:")); | ||
| 52 | + var clrObject = (CLRObject)ManagedType.GetManagedObject(uri.Handle); | ||
| 53 | + Assert.AreSame(encoder1, clrObject.inst); | ||
| 54 | + Assert.AreNotSame(encoder2, clrObject.inst); | ||
| 55 | + | ||
| 56 | + var tuple = group.TryEncode(Tuple.Create(1)); | ||
| 57 | + clrObject = (CLRObject)ManagedType.GetManagedObject(tuple.Handle); | ||
| 58 | + Assert.AreSame(encoder0, clrObject.inst); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + [Test] | ||
| 62 | + public void GetDecodersByTypes() | ||
| 63 | + { | ||
| 64 | + var pyint = new PyInt(10).GetPythonType(); | ||
| 65 | + var pyfloat = new PyFloat(10).GetPythonType(); | ||
| 66 | + var pystr = new PyString("world").GetPythonType(); | ||
| 67 | + var decoder1 = new DecoderReturningPredefinedValue<long>(pyint, decodeResult: 42); | ||
| 68 | + var decoder2 = new DecoderReturningPredefinedValue<string>(pyfloat, decodeResult: "atad:"); | ||
| 69 | + var group = new DecoderGroup { | ||
| 70 | + decoder1, | ||
| 71 | + decoder2, | ||
| 72 | + }; | ||
| 73 | + | ||
| 74 | + var decoder = group.GetDecoder(pyfloat, typeof(string)); | ||
| 75 | + Assert.AreSame(decoder2, decoder); | ||
| 76 | + decoder = group.GetDecoder(pystr, typeof(string)); | ||
| 77 | + Assert.IsNull(decoder); | ||
| 78 | + decoder = group.GetDecoder(pyint, typeof(long)); | ||
| 79 | + Assert.AreSame(decoder1, decoder); | ||
| 80 | + } | ||
| 81 | + [Test] | ||
| 82 | + public void CanDecode() | ||
| 83 | + { | ||
| 84 | + var pyint = new PyInt(10).GetPythonType(); | ||
| 85 | + var pyfloat = new PyFloat(10).GetPythonType(); | ||
| 86 | + var pystr = new PyString("world").GetPythonType(); | ||
| 87 | + var decoder1 = new DecoderReturningPredefinedValue<long>(pyint, decodeResult: 42); | ||
| 88 | + var decoder2 = new DecoderReturningPredefinedValue<string>(pyfloat, decodeResult: "atad:"); | ||
| 89 | + var group = new DecoderGroup { | ||
| 90 | + decoder1, | ||
| 91 | + decoder2, | ||
| 92 | + }; | ||
| 93 | + | ||
| 94 | + Assert.IsTrue(group.CanDecode(pyint, typeof(long))); | ||
| 95 | + Assert.IsFalse(group.CanDecode(pyint, typeof(int))); | ||
| 96 | + Assert.IsTrue(group.CanDecode(pyfloat, typeof(string))); | ||
| 97 | + Assert.IsFalse(group.CanDecode(pystr, typeof(string))); | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + [Test] | ||
| 101 | + public void Decodes() | ||
| 102 | + { | ||
| 103 | + var pyint = new PyInt(10).GetPythonType(); | ||
| 104 | + var pyfloat = new PyFloat(10).GetPythonType(); | ||
| 105 | + var decoder1 = new DecoderReturningPredefinedValue<long>(pyint, decodeResult: 42); | ||
| 106 | + var decoder2 = new DecoderReturningPredefinedValue<string>(pyfloat, decodeResult: "atad:"); | ||
| 107 | + var group = new DecoderGroup { | ||
| 108 | + decoder1, | ||
| 109 | + decoder2, | ||
| 110 | + }; | ||
| 111 | + | ||
| 112 | + Assert.IsTrue(group.TryDecode(new PyInt(10), out long longResult)); | ||
| 113 | + Assert.AreEqual(42, longResult); | ||
| 114 | + Assert.IsTrue(group.TryDecode(new PyFloat(10), out string strResult)); | ||
| 115 | + Assert.AreSame("atad:", strResult); | ||
| 116 | + | ||
| 117 | + Assert.IsFalse(group.TryDecode(new PyInt(10), out int _)); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + [SetUp] | ||
| 121 | + public void SetUp() | ||
| 122 | + { | ||
| 123 | + PythonEngine.Initialize(); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + [TearDown] | ||
| 127 | + public void Dispose() | ||
| 128 | + { | ||
| 129 | + PythonEngine.Shutdown(); | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -83,4 +83,41 @@ static void TupleRoundtripGeneric<T, TTuple>() { | |||
| 83 | 83 | } | |
| 84 | 84 | } | |
| 85 | 85 | } | |
| 86 | + | ||
| 87 | + /// <summary> | ||
| 88 | + /// "Decodes" only objects of exact type <typeparamref name="T"/>. | ||
| 89 | + /// Result is just a raw Python object proxy. | ||
| 90 | + /// </summary> | ||
| 91 | + class ObjectToRawProxyEncoder<T> : IPyObjectEncoder | ||
| 92 | + { | ||
| 93 | + public bool CanEncode(Type type) => type == typeof(T); | ||
| 94 | + public PyObject TryEncode(object value) => this.GetRawPythonProxy(); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + /// <summary> | ||
| 98 | + /// Decodes object of specified Python type to the predefined value <see cref="DecodeResult"/> | ||
| 99 | + /// </summary> | ||
| 100 | + /// <typeparam name="TTarget">Type of the <see cref="DecodeResult"/></typeparam> | ||
| 101 | + class DecoderReturningPredefinedValue<TTarget> : IPyObjectDecoder | ||
| 102 | + { | ||
| 103 | + public PyObject TheOnlySupportedSourceType { get; } | ||
| 104 | + public TTarget DecodeResult { get; } | ||
| 105 | + | ||
| 106 | + public DecoderReturningPredefinedValue(PyObject objectType, TTarget decodeResult) | ||
| 107 | + { | ||
| 108 | + this.TheOnlySupportedSourceType = objectType; | ||
| 109 | + this.DecodeResult = decodeResult; | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + public bool CanDecode(PyObject objectType, Type targetType) | ||
| 113 | + => objectType.Handle == TheOnlySupportedSourceType.Handle | ||
| 114 | + && targetType == typeof(TTarget); | ||
| 115 | + public bool TryDecode<T>(PyObject pyObj, out T value) | ||
| 116 | + { | ||
| 117 | + if (typeof(T) != typeof(TTarget)) | ||
| 118 | + throw new ArgumentException(nameof(T)); | ||
| 119 | + value = (T)(object)DecodeResult; | ||
| 120 | + return true; | ||
| 121 | + } | ||
| 122 | + } | ||
| 86 | 123 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -83,6 +83,7 @@ | |||
| 83 | 83 | <None Include="packages.config" /> | |
| 84 | 84 | </ItemGroup> | |
| 85 | 85 | <ItemGroup> | |
| 86 | + <Compile Include="CodecGroups.cs" /> | ||
| 86 | 87 | <Compile Include="Codecs.cs" /> | |
| 87 | 88 | <Compile Include="dynamic.cs" /> | |
| 88 | 89 | <Compile Include="pyimport.cs" /> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,78 @@ | |||
| 1 | + namespace Python.Runtime.Codecs | ||
| 2 | + { | ||
| 3 | + using System; | ||
| 4 | + using System.Collections; | ||
| 5 | + using System.Collections.Generic; | ||
| 6 | + using System.Linq; | ||
| 7 | + | ||
| 8 | + /// <summary> | ||
| 9 | + /// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority. | ||
| 10 | + /// </summary> | ||
| 11 | + [Obsolete(Util.UnstableApiMessage)] | ||
| 12 | + public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable<IPyObjectDecoder> | ||
| 13 | + { | ||
| 14 | + readonly List<IPyObjectDecoder> decoders = new List<IPyObjectDecoder>(); | ||
| 15 | + | ||
| 16 | + /// <summary> | ||
| 17 | + /// Add specified decoder to the group | ||
| 18 | + /// </summary> | ||
| 19 | + public void Add(IPyObjectDecoder item) | ||
| 20 | + { | ||
| 21 | + if (item is null) throw new ArgumentNullException(nameof(item)); | ||
| 22 | + | ||
| 23 | + this.decoders.Add(item); | ||
| 24 | + } | ||
| 25 | + /// <summary> | ||
| 26 | + /// Remove all decoders from the group | ||
| 27 | + /// </summary> | ||
| 28 | + public void Clear() => this.decoders.Clear(); | ||
| 29 | + | ||
| 30 | + /// <inheritdoc /> | ||
| 31 | + public bool CanDecode(PyObject objectType, Type targetType) | ||
| 32 | + => this.decoders.Any(decoder => decoder.CanDecode(objectType, targetType)); | ||
| 33 | + /// <inheritdoc /> | ||
| 34 | + public bool TryDecode<T>(PyObject pyObj, out T value) | ||
| 35 | + { | ||
| 36 | + if (pyObj is null) throw new ArgumentNullException(nameof(pyObj)); | ||
| 37 | + | ||
| 38 | + var decoder = this.GetDecoder(pyObj.GetPythonType(), typeof(T)); | ||
| 39 | + if (decoder is null) | ||
| 40 | + { | ||
| 41 | + value = default; | ||
| 42 | + return false; | ||
| 43 | + } | ||
| 44 | + return decoder.TryDecode(pyObj, out value); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + /// <inheritdoc /> | ||
| 48 | + public IEnumerator<IPyObjectDecoder> GetEnumerator() => this.decoders.GetEnumerator(); | ||
| 49 | + IEnumerator IEnumerable.GetEnumerator() => this.decoders.GetEnumerator(); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + [Obsolete(Util.UnstableApiMessage)] | ||
| 53 | + public static class DecoderGroupExtensions | ||
| 54 | + { | ||
| 55 | + /// <summary> | ||
| 56 | + /// Gets a concrete instance of <see cref="IPyObjectDecoder"/> | ||
| 57 | + /// (potentially selecting one from a collection), | ||
| 58 | + /// that can decode from <paramref name="objectType"/> to <paramref name="targetType"/>, | ||
| 59 | + /// or <c>null</c> if a matching decoder can not be found. | ||
| 60 | + /// </summary> | ||
| 61 | + [Obsolete(Util.UnstableApiMessage)] | ||
| 62 | + public static IPyObjectDecoder GetDecoder( | ||
| 63 | + this IPyObjectDecoder decoder, | ||
| 64 | + PyObject objectType, Type targetType) | ||
| 65 | + { | ||
| 66 | + if (decoder is null) throw new ArgumentNullException(nameof(decoder)); | ||
| 67 | + | ||
| 68 | + if (decoder is IEnumerable<IPyObjectDecoder> composite) | ||
| 69 | + { | ||
| 70 | + return composite | ||
| 71 | + .Select(nestedDecoder => nestedDecoder.GetDecoder(objectType, targetType)) | ||
| 72 | + .FirstOrDefault(d => d != null); | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + return decoder.CanDecode(objectType, targetType) ? decoder : null; | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,79 @@ | |||
| 1 | + namespace Python.Runtime.Codecs | ||
| 2 | + { | ||
| 3 | + using System; | ||
| 4 | + using System.Collections; | ||
| 5 | + using System.Collections.Generic; | ||
| 6 | + using System.Linq; | ||
| 7 | + | ||
| 8 | + /// <summary> | ||
| 9 | + /// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority. | ||
| 10 | + /// </summary> | ||
| 11 | + [Obsolete(Util.UnstableApiMessage)] | ||
| 12 | + public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable<IPyObjectEncoder> | ||
| 13 | + { | ||
| 14 | + readonly List<IPyObjectEncoder> encoders = new List<IPyObjectEncoder>(); | ||
| 15 | + | ||
| 16 | + /// <summary> | ||
| 17 | + /// Add specified encoder to the group | ||
| 18 | + /// </summary> | ||
| 19 | + public void Add(IPyObjectEncoder item) | ||
| 20 | + { | ||
| 21 | + if (item is null) throw new ArgumentNullException(nameof(item)); | ||
| 22 | + this.encoders.Add(item); | ||
| 23 | + } | ||
| 24 | + /// <summary> | ||
| 25 | + /// Remove all encoders from the group | ||
| 26 | + /// </summary> | ||
| 27 | + public void Clear() => this.encoders.Clear(); | ||
| 28 | + | ||
| 29 | + /// <inheritdoc /> | ||
| 30 | + public bool CanEncode(Type type) => this.encoders.Any(encoder => encoder.CanEncode(type)); | ||
| 31 | + /// <inheritdoc /> | ||
| 32 | + public PyObject TryEncode(object value) | ||
| 33 | + { | ||
| 34 | + if (value is null) throw new ArgumentNullException(nameof(value)); | ||
| 35 | + | ||
| 36 | + foreach (var encoder in this.GetEncoders(value.GetType())) | ||
| 37 | + { | ||
| 38 | + var result = encoder.TryEncode(value); | ||
| 39 | + if (result != null) | ||
| 40 | + { | ||
| 41 | + return result; | ||
| 42 | + } | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + return null; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + /// <inheritdoc /> | ||
| 49 | + public IEnumerator<IPyObjectEncoder> GetEnumerator() => this.encoders.GetEnumerator(); | ||
| 50 | + IEnumerator IEnumerable.GetEnumerator() => this.encoders.GetEnumerator(); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + [Obsolete(Util.UnstableApiMessage)] | ||
| 54 | + public static class EncoderGroupExtensions | ||
| 55 | + { | ||
| 56 | + /// <summary> | ||
| 57 | + /// Gets specific instances of <see cref="IPyObjectEncoder"/> | ||
| 58 | + /// (potentially selecting one from a collection), | ||
| 59 | + /// that can encode the specified <paramref name="type"/>. | ||
| 60 | + /// </summary> | ||
| 61 | + [Obsolete(Util.UnstableApiMessage)] | ||
| 62 | + public static IEnumerable<IPyObjectEncoder> GetEncoders(this IPyObjectEncoder decoder, Type type) | ||
| 63 | + { | ||
| 64 | + if (decoder is null) throw new ArgumentNullException(nameof(decoder)); | ||
| 65 | + | ||
| 66 | + if (decoder is IEnumerable<IPyObjectEncoder> composite) | ||
| 67 | + { | ||
| 68 | + foreach (var nestedEncoder in composite) | ||
| 69 | + foreach (var match in nestedEncoder.GetEncoders(type)) | ||
| 70 | + { | ||
| 71 | + yield return match; | ||
| 72 | + } | ||
| 73 | + } else if (decoder.CanEncode(type)) | ||
| 74 | + { | ||
| 75 | + yield return decoder; | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -76,6 +76,8 @@ | |||
| 76 | 76 | <Reference Include="System" /> | |
| 77 | 77 | </ItemGroup> | |
| 78 | 78 | <ItemGroup> | |
| 79 | + <Compile Include="Codecs\EncoderGroup.cs" /> | ||
| 80 | + <Compile Include="Codecs\DecoderGroup.cs" /> | ||
| 79 | 81 | <Compile Include="Codecs\TupleCodecs.cs" /> | |
| 80 | 82 | <Compile Include="converterextensions.cs" /> | |
| 81 | 83 | <Compile Include="finalizer.cs" /> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,6 +5,7 @@ namespace Python.Runtime | |||
| 5 | 5 | using System.Collections.Generic; | |
| 6 | 6 | using System.Linq; | |
| 7 | 7 | using System.Reflection; | |
| 8 | + using Python.Runtime.Codecs; | ||
| 8 | 9 | ||
| 9 | 10 | /// <summary> | |
| 10 | 11 | /// Defines <see cref="PyObject"/> conversion to CLR types (unmarshalling) | |
@@ -49,8 +50,8 @@ public interface IPyObjectEncoder | |||
| 49 | 50 | [Obsolete(Util.UnstableApiMessage)] | |
| 50 | 51 | public static class PyObjectConversions | |
| 51 | 52 | { | |
| 52 | - static readonly List<IPyObjectDecoder> decoders = new List<IPyObjectDecoder>(); | ||
| 53 | - static readonly List<IPyObjectEncoder> encoders = new List<IPyObjectEncoder>(); | ||
| 53 | + static readonly DecoderGroup decoders = new DecoderGroup(); | ||
| 54 | + static readonly EncoderGroup encoders = new EncoderGroup(); | ||
| 54 | 55 | ||
| 55 | 56 | /// <summary> | |
| 56 | 57 | /// Registers specified encoder (marshaller) | |
@@ -101,7 +102,7 @@ static IPyObjectEncoder[] GetEncoders(Type type) | |||
| 101 | 102 | { | |
| 102 | 103 | lock (encoders) | |
| 103 | 104 | { | |
| 104 | - return encoders.Where(encoder => encoder.CanEncode(type)).ToArray(); | ||
| 105 | + return encoders.GetEncoders(type).ToArray(); | ||
| 105 | 106 | } | |
| 106 | 107 | } | |
| 107 | 108 | #endregion | |
@@ -128,7 +129,7 @@ static Converter.TryConvertFromPythonDelegate GetDecoder(IntPtr sourceType, Type | |||
| 128 | 129 | { | |
| 129 | 130 | lock (decoders) | |
| 130 | 131 | { | |
| 131 | - decoder = decoders.Find(d => d.CanDecode(pyType, targetType)); | ||
| 132 | + decoder = decoders.GetDecoder(pyType, targetType); | ||
| 132 | 133 | if (decoder == null) return null; | |
| 133 | 134 | } | |
| 134 | 135 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments