Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IronPython function with variable number of arguments as a delegate

In IronPython I am trying to call a PythonFunction with different numbers of arguments from C#. For instance;

I want to do:

def foo(a, b):
     print a, b

def bar(a, b, c = None):
     print a, b, c

p = App.DynamicEvent()
p.addHandler(foo)
p.addHandler(bar)
p.invoke("Not", "Working")

where addHandler takes a single argument and somehow stores it in a list of methods to be invoked and invoke has a signature like this:

public virtual void invoke(params object[] tArgs)

Because I want to avoid making it specific to the PythonEngine (and thus engine.Operations.Invoke()), I've tried several ways of storing and implementing these things as delegates but I think the crux of my problem is that I don't know how to store some kind of MulticastDelegate base type that is compatible with a PythonFunction?

Perhaps I want to implement my own DynamicInvoke method? Any thoughts and experience would be greatly appreciated!

The reason for wanting to do this is that I want to transparently map calls made from a sealed Javascript engine into IronPython via C#. i.e. in the Javascript call: Client.doThing("something", 4, {"key:"value"}) and handle it in the python with:

def doThing(s, i, d):
    pass

using the following dynamic event binding:

doThingEvent = App.DynamicEvent()
doThingEvent.addHandler(doThing)
WebBrowser.handleMethod("doThing", doThingEvent);
like image 994
John Avatar asked May 27 '26 10:05

John


1 Answers

You can pass a PythonFunction as a delegate for example by casting to an Action<...>

i.e. in Python by doing something like:

import sys
import clr
import System
from System import *

def foo(a, b):
     print a, b

def bar(a, b, c = "N.A."):
     print a, b, c

p = App.DynamicEvent()
p.AddHandler( Action[object,object](foo) )
p.AddHandler( Action[object,object,object](bar) )
p.DynamicInvoke("Not", "Working")

In this way your p can be a MulticastDelegate. Obviously this implies:

  1. All the passed delegates must have the same signature
  2. you can pass Python function with at most 16 parameters (because Action support 16 params maximum)

For the first problem I think you need to write your own "Delegates invoker", something like:

//
// WARNING: very very rough code here !!
//
public class DynamicEvent
{
    List<Delegate> delegates;

    public DynamicEvent()
    {
        delegates = new List<Delegate>();
    }
    public void AddHandler(Delegate dlgt)
    {
        delegates.Add(dlgt);
    }
    public void Invoke(params object[] args)
    {
        foreach (var del in delegates)
        {
            var parameters = del.Method.GetParameters();
            // check parameters number
            if (args.Length != parameters.Length)
                continue; // go to next param

            // check parameters assignability
            bool assignable = true;
            for (int i = 0; i < args.Length; i++)
            {
                if (!parameters[i].ParameterType.IsInstanceOfType(args[i]))
                {
                    assignable = false;
                    break; // stop looping on parameters
                }
            }
            if (!assignable)
                continue; // go to next param

            // OK it seems compatible, let's invoke
            del.DynamicInvoke(args);
        }
    }
}

N.B.:

the part checking delegate-parameters compatibility is wrong, is just to give you an idea.

The problem is that I don't know how to check, at runtime, whether a list of object args is compatible with a delegate signature... maybe we should check IronPython source code :)

like image 107
digEmAll Avatar answered May 30 '26 09:05

digEmAll



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!