Skip to content

Add codecs for functions #1080

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
attempt to add python function decoder
  • Loading branch information
koubaa committed Mar 3, 2020
commit 668d6394a1d2978bbce939fb0775312eb5112d48
63 changes: 48 additions & 15 deletions src/embed_tests/Codecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,34 @@ namespace Python.EmbeddingTest {
using Python.Runtime;
using Python.Runtime.Codecs;

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

[TearDown]
public void Dispose() {
public void Dispose()
{
PythonEngine.Shutdown();
}

[Test]
public void ConversionsGeneric() {
public void ConversionsGeneric()
{
ConversionsGeneric<ValueTuple<int, string, object>, ValueTuple>();
}

static void ConversionsGeneric<T, TTuple>() {
static void ConversionsGeneric<T, TTuple>()
{
TupleCodec<TTuple>.Register();
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
T restored = default;
using (Py.GIL())
using (var scope = Py.CreateScope()) {
using (var scope = Py.CreateScope())
{
void Accept(T value) => restored = value;
var accept = new Action<T>(Accept).ToPython();
scope.Set(nameof(tuple), tuple);
Expand All @@ -38,15 +44,18 @@ static void ConversionsGeneric<T, TTuple>() {
}

[Test]
public void ConversionsObject() {
public void ConversionsObject()
{
ConversionsObject<ValueTuple<int, string, object>, ValueTuple>();
}
static void ConversionsObject<T, TTuple>() {
static void ConversionsObject<T, TTuple>()
{
TupleCodec<TTuple>.Register();
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
T restored = default;
using (Py.GIL())
using (var scope = Py.CreateScope()) {
using (var scope = Py.CreateScope())
{
void Accept(object value) => restored = (T)value;
var accept = new Action<object>(Accept).ToPython();
scope.Set(nameof(tuple), tuple);
Expand All @@ -57,30 +66,54 @@ static void ConversionsObject<T, TTuple>() {
}

[Test]
public void TupleRoundtripObject() {
public void TupleRoundtripObject()
{
TupleRoundtripObject<ValueTuple<int, string, object>, ValueTuple>();
}
static void TupleRoundtripObject<T, TTuple>() {
static void TupleRoundtripObject<T, TTuple>()
{
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
using (Py.GIL()) {
using (Py.GIL())
{
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out object restored));
Assert.AreEqual(expected: tuple, actual: restored);
}
}

[Test]
public void TupleRoundtripGeneric() {
public void TupleRoundtripGeneric()
{
TupleRoundtripGeneric<ValueTuple<int, string, object>, ValueTuple>();
}

static void TupleRoundtripGeneric<T, TTuple>() {
static void TupleRoundtripGeneric<T, TTuple>()
{
var tuple = Activator.CreateInstance(typeof(T), 42, "42", new object());
using (Py.GIL()) {
using (Py.GIL())
{
var pyTuple = TupleCodec<TTuple>.Instance.TryEncode(tuple);
Assert.IsTrue(TupleCodec<TTuple>.Instance.TryDecode(pyTuple, out T restored));
Assert.AreEqual(expected: tuple, actual: restored);
}
}

[Test]
public void Function()
{
FunctionCodec.Register();
var locals = new PyDict();

PythonEngine.Exec(@"
def foo():
return 1
", null, locals.Handle);

var func = locals.GetItem("foo");

Action action;
Assert.IsTrue(FunctionCodec.Instance.TryDecode(func, out action));
Assert.DoesNotThrow(() => action());
}
}
}
44 changes: 44 additions & 0 deletions src/runtime/Codecs/FunctionCodec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Python.Runtime.Codecs
{
//converts python functions to C# actions
class FunctionCodec : IPyObjectDecoder
{
public static FunctionCodec Instance { get; } = new FunctionCodec();
public bool CanDecode(PyObject objectType, Type targetType)
{
if (!objectType.IsCallable()) return false;

//TODO - handle nonzero arguments.
var args = targetType.GetGenericArguments();
return args.Length == 0;
}

public bool TryDecode<T>(PyObject pyObj, out T value)
{
Action action = () =>
{
Runtime.XIncref(pyObj.Handle);
PyObject pyAction = new PyObject(pyObj.Handle);
var pyArgs = new PyObject[0];
using (Py.GIL())
{
var pyResult = pyAction.Invoke(pyArgs);
Runtime.XIncref(pyResult.Handle);
}
};
var v = (object)action;
value = (T)v;
return true;
}

public static void Register()
{
PyObjectConversions.RegisterDecoder(Instance);
}
}
}