Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Codec groups: EncoderGroup and DecoderGroup#1085

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
filmor merged 2 commits intopythonnet:masterfromlosttech:PR/CodecGroups
Apr 22, 2020
Merged
Show file tree
Hide file tree
Changes fromall 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
132 changes: 132 additions & 0 deletionssrc/embed_tests/CodecGroups.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
namespace Python.EmbeddingTest
{
using System;
using System.Linq;
using NUnit.Framework;
using Python.Runtime;
using Python.Runtime.Codecs;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

While I don't have a particular problem with this, why are you using this style? It's not in use in the established codebase and I have never seen it outside either.

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Imagine we use aSystem.Uri a lot. At some point in the future we decide to take a dependency (or introduce our own) on something, that hasPython.Uri class, which we might not need ourselves, but it is public.

With

usingSystem;namespacePython.Runtime{classSomeClass{UriuriField;}}

theuriField will silently change its type fromSystem.Uri toPython.Uri.

If theusing System; is insidenamespace Python.Runtime, that won't happen.


public class CodecGroups
{
[Test]
public void GetEncodersByType()
{
var encoder1 = new ObjectToRawProxyEncoder<Uri>();
var encoder2 = new ObjectToRawProxyEncoder<Uri>();
var group = new EncoderGroup {
new ObjectToRawProxyEncoder<Tuple<int>>(),
encoder1,
encoder2,
};

var got = group.GetEncoders(typeof(Uri)).ToArray();
CollectionAssert.AreEqual(new[]{encoder1, encoder2}, got);
}

[Test]
public void CanEncode()
{
var group = new EncoderGroup {
new ObjectToRawProxyEncoder<Tuple<int>>(),
new ObjectToRawProxyEncoder<Uri>(),
};

Assert.IsTrue(group.CanEncode(typeof(Tuple<int>)));
Assert.IsTrue(group.CanEncode(typeof(Uri)));
Assert.IsFalse(group.CanEncode(typeof(string)));
}

[Test]
public void Encodes()
{
var encoder0 = new ObjectToRawProxyEncoder<Tuple<int>>();
var encoder1 = new ObjectToRawProxyEncoder<Uri>();
var encoder2 = new ObjectToRawProxyEncoder<Uri>();
var group = new EncoderGroup {
encoder0,
encoder1,
encoder2,
};

var uri = group.TryEncode(new Uri("data:"));
var clrObject = (CLRObject)ManagedType.GetManagedObject(uri.Handle);
Assert.AreSame(encoder1, clrObject.inst);
Assert.AreNotSame(encoder2, clrObject.inst);

var tuple = group.TryEncode(Tuple.Create(1));
clrObject = (CLRObject)ManagedType.GetManagedObject(tuple.Handle);
Assert.AreSame(encoder0, clrObject.inst);
}

[Test]
public void GetDecodersByTypes()
{
var pyint = new PyInt(10).GetPythonType();
var pyfloat = new PyFloat(10).GetPythonType();
var pystr = new PyString("world").GetPythonType();
var decoder1 = new DecoderReturningPredefinedValue<long>(pyint, decodeResult: 42);
var decoder2 = new DecoderReturningPredefinedValue<string>(pyfloat, decodeResult: "atad:");
var group = new DecoderGroup {
decoder1,
decoder2,
};

var decoder = group.GetDecoder(pyfloat, typeof(string));
Assert.AreSame(decoder2, decoder);
decoder = group.GetDecoder(pystr, typeof(string));
Assert.IsNull(decoder);
decoder = group.GetDecoder(pyint, typeof(long));
Assert.AreSame(decoder1, decoder);
}
[Test]
public void CanDecode()
{
var pyint = new PyInt(10).GetPythonType();
var pyfloat = new PyFloat(10).GetPythonType();
var pystr = new PyString("world").GetPythonType();
var decoder1 = new DecoderReturningPredefinedValue<long>(pyint, decodeResult: 42);
var decoder2 = new DecoderReturningPredefinedValue<string>(pyfloat, decodeResult: "atad:");
var group = new DecoderGroup {
decoder1,
decoder2,
};

Assert.IsTrue(group.CanDecode(pyint, typeof(long)));
Assert.IsFalse(group.CanDecode(pyint, typeof(int)));
Assert.IsTrue(group.CanDecode(pyfloat, typeof(string)));
Assert.IsFalse(group.CanDecode(pystr, typeof(string)));
}

[Test]
public void Decodes()
{
var pyint = new PyInt(10).GetPythonType();
var pyfloat = new PyFloat(10).GetPythonType();
var decoder1 = new DecoderReturningPredefinedValue<long>(pyint, decodeResult: 42);
var decoder2 = new DecoderReturningPredefinedValue<string>(pyfloat, decodeResult: "atad:");
var group = new DecoderGroup {
decoder1,
decoder2,
};

Assert.IsTrue(group.TryDecode(new PyInt(10), out long longResult));
Assert.AreEqual(42, longResult);
Assert.IsTrue(group.TryDecode(new PyFloat(10), out string strResult));
Assert.AreSame("atad:", strResult);

Assert.IsFalse(group.TryDecode(new PyInt(10), out int _));
}

[SetUp]
public void SetUp()
{
PythonEngine.Initialize();
}

[TearDown]
public void Dispose()
{
PythonEngine.Shutdown();
}
}
}
37 changes: 37 additions & 0 deletionssrc/embed_tests/Codecs.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -83,4 +83,41 @@ static void TupleRoundtripGeneric<T, TTuple>() {
}
}
}

/// <summary>
/// "Decodes" only objects of exact type <typeparamref name="T"/>.
/// Result is just a raw Python object proxy.
/// </summary>
class ObjectToRawProxyEncoder<T> : IPyObjectEncoder
{
public bool CanEncode(Type type) => type == typeof(T);
public PyObject TryEncode(object value) => this.GetRawPythonProxy();
}

/// <summary>
/// Decodes object of specified Python type to the predefined value <see cref="DecodeResult"/>
/// </summary>
/// <typeparam name="TTarget">Type of the <see cref="DecodeResult"/></typeparam>
class DecoderReturningPredefinedValue<TTarget> : IPyObjectDecoder
{
public PyObject TheOnlySupportedSourceType { get; }
public TTarget DecodeResult { get; }

public DecoderReturningPredefinedValue(PyObject objectType, TTarget decodeResult)
{
this.TheOnlySupportedSourceType = objectType;
this.DecodeResult = decodeResult;
}

public bool CanDecode(PyObject objectType, Type targetType)
=> objectType.Handle == TheOnlySupportedSourceType.Handle
&& targetType == typeof(TTarget);
public bool TryDecode<T>(PyObject pyObj, out T value)
{
if (typeof(T) != typeof(TTarget))
throw new ArgumentException(nameof(T));
value = (T)(object)DecodeResult;
return true;
}
}
}
1 change: 1 addition & 0 deletionssrc/embed_tests/Python.EmbeddingTest.csproj
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -83,6 +83,7 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Compile Include="CodecGroups.cs" />
<Compile Include="Codecs.cs" />
<Compile Include="dynamic.cs" />
<Compile Include="pyimport.cs" />
Expand Down
78 changes: 78 additions & 0 deletionssrc/runtime/Codecs/DecoderGroup.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
namespace Python.Runtime.Codecs
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority.
/// </summary>
[Obsolete(Util.UnstableApiMessage)]
public sealed class DecoderGroup: IPyObjectDecoder, IEnumerable<IPyObjectDecoder>
{
readonly List<IPyObjectDecoder> decoders = new List<IPyObjectDecoder>();

/// <summary>
/// Add specified decoder to the group
/// </summary>
public void Add(IPyObjectDecoder item)
{
if (item is null) throw new ArgumentNullException(nameof(item));

this.decoders.Add(item);
}
/// <summary>
/// Remove all decoders from the group
/// </summary>
public void Clear() => this.decoders.Clear();

/// <inheritdoc />
public bool CanDecode(PyObject objectType, Type targetType)
=> this.decoders.Any(decoder => decoder.CanDecode(objectType, targetType));
/// <inheritdoc />
public bool TryDecode<T>(PyObject pyObj, out T value)
{
if (pyObj is null) throw new ArgumentNullException(nameof(pyObj));

var decoder = this.GetDecoder(pyObj.GetPythonType(), typeof(T));
if (decoder is null)
{
value = default;
return false;
}
return decoder.TryDecode(pyObj, out value);
}

/// <inheritdoc />
public IEnumerator<IPyObjectDecoder> GetEnumerator() => this.decoders.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.decoders.GetEnumerator();
}

[Obsolete(Util.UnstableApiMessage)]
public static class DecoderGroupExtensions
{
/// <summary>
/// Gets a concrete instance of <see cref="IPyObjectDecoder"/>
/// (potentially selecting one from a collection),
/// that can decode from <paramref name="objectType"/> to <paramref name="targetType"/>,
/// or <c>null</c> if a matching decoder can not be found.
/// </summary>
[Obsolete(Util.UnstableApiMessage)]
public static IPyObjectDecoder GetDecoder(
this IPyObjectDecoder decoder,
PyObject objectType, Type targetType)
{
if (decoder is null) throw new ArgumentNullException(nameof(decoder));

if (decoder is IEnumerable<IPyObjectDecoder> composite)
{
return composite
.Select(nestedDecoder => nestedDecoder.GetDecoder(objectType, targetType))
.FirstOrDefault(d => d != null);
}

return decoder.CanDecode(objectType, targetType) ? decoder : null;
}
}
}
79 changes: 79 additions & 0 deletionssrc/runtime/Codecs/EncoderGroup.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
namespace Python.Runtime.Codecs
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Represents a group of <see cref="IPyObjectDecoder"/>s. Useful to group them by priority.
/// </summary>
[Obsolete(Util.UnstableApiMessage)]
public sealed class EncoderGroup: IPyObjectEncoder, IEnumerable<IPyObjectEncoder>
{
readonly List<IPyObjectEncoder> encoders = new List<IPyObjectEncoder>();

/// <summary>
/// Add specified encoder to the group
/// </summary>
public void Add(IPyObjectEncoder item)
{
if (item is null) throw new ArgumentNullException(nameof(item));
this.encoders.Add(item);
}
/// <summary>
/// Remove all encoders from the group
/// </summary>
public void Clear() => this.encoders.Clear();

/// <inheritdoc />
public bool CanEncode(Type type) => this.encoders.Any(encoder => encoder.CanEncode(type));
/// <inheritdoc />
public PyObject TryEncode(object value)
{
if (value is null) throw new ArgumentNullException(nameof(value));

foreach (var encoder in this.GetEncoders(value.GetType()))
{
var result = encoder.TryEncode(value);
if (result != null)
{
return result;
}
}

return null;
}

/// <inheritdoc />
public IEnumerator<IPyObjectEncoder> GetEnumerator() => this.encoders.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.encoders.GetEnumerator();
}

[Obsolete(Util.UnstableApiMessage)]
public static class EncoderGroupExtensions
{
/// <summary>
/// Gets specific instances of <see cref="IPyObjectEncoder"/>
/// (potentially selecting one from a collection),
/// that can encode the specified <paramref name="type"/>.
/// </summary>
[Obsolete(Util.UnstableApiMessage)]
public static IEnumerable<IPyObjectEncoder> GetEncoders(this IPyObjectEncoder decoder, Type type)
{
if (decoder is null) throw new ArgumentNullException(nameof(decoder));

if (decoder is IEnumerable<IPyObjectEncoder> composite)
{
foreach (var nestedEncoder in composite)
foreach (var match in nestedEncoder.GetEncoders(type))
{
yield return match;
}
} else if (decoder.CanEncode(type))
{
yield return decoder;
}
}
}
}
2 changes: 2 additions & 0 deletionssrc/runtime/Python.Runtime.csproj
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Codecs\EncoderGroup.cs" />
<Compile Include="Codecs\DecoderGroup.cs" />
<Compile Include="Codecs\TupleCodecs.cs" />
<Compile Include="converterextensions.cs" />
<Compile Include="finalizer.cs" />
Expand Down
9 changes: 5 additions & 4 deletionssrc/runtime/converterextensions.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ namespace Python.Runtime
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Python.Runtime.Codecs;

/// <summary>
/// Defines <see cref="PyObject"/> conversion to CLR types (unmarshalling)
Expand DownExpand Up@@ -49,8 +50,8 @@ public interface IPyObjectEncoder
[Obsolete(Util.UnstableApiMessage)]
public static class PyObjectConversions
{
static readonlyList<IPyObjectDecoder> decoders = newList<IPyObjectDecoder>();
static readonlyList<IPyObjectEncoder> encoders = newList<IPyObjectEncoder>();
static readonlyDecoderGroup decoders = newDecoderGroup();
static readonlyEncoderGroup encoders = newEncoderGroup();

/// <summary>
/// Registers specified encoder (marshaller)
Expand DownExpand Up@@ -101,7 +102,7 @@ static IPyObjectEncoder[] GetEncoders(Type type)
{
lock (encoders)
{
return encoders.Where(encoder => encoder.CanEncode(type)).ToArray();
return encoders.GetEncoders(type).ToArray();
}
}
#endregion
Expand All@@ -128,7 +129,7 @@ static Converter.TryConvertFromPythonDelegate GetDecoder(IntPtr sourceType, Type
{
lock (decoders)
{
decoder = decoders.Find(d => d.CanDecode(pyType, targetType));
decoder = decoders.GetDecoder(pyType, targetType);
if (decoder == null) return null;
}
}
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp