Search code examples
c#.netironpythondynamicobject

IronPython calls TryGetMember instead of TryInvokeMember


i'm trying to hand over a Dynamic object to Ironpython, but it seems Ironpython is not calling TryInvokeMember. Instead it calls TryGetMember and gives an Error that it cant call the result.

I have tried it with IronPython 2.7 and 2.6.10920

ExampleCode:

DynamicObject:

class ExampleDynamicObject: DynamicObject {
    public override bool TryGetMember(GetMemberBinder binder,
    out object result) {
        result = "TryGetMember";
        return true;
    }
    public override bool TryInvokeMember(InvokeMemberBinder binder,
    object[] args,
    out object result) {
        result = "TryInvokeMember";
        return true;
    }
}

calling Mathode

static void Main(string[] args) {
    dynamic example = new ExampleDynamicObject();
    var program = @"test = example.Call2(2)";

    var engine = Python.CreateEngine();
    var scope = engine.CreateScope();
    scope.SetVariable("example", example);
    var source = engine.CreateScriptSourceFromString(program,
    SourceCodeKind.Statements);
    source.Execute(scope);
    Console.ReadKey();
}

This calls the TryGetMember method and then throws a Microsoft.Scripting.ArgumentTypeException "str is not callable"

This is thrown when u code something like 'test'(1)

So it seems that Python doesn't get that this is a Function call and instead just calls a Property.

But when i try to call it from C#

    Console.WriteLine(example.Call);
    Console.WriteLine("----------------------------");
    Console.WriteLine(example.Call(1));

This will work:

TryGetMember
-------------------
TryInvokeMember

Has anyone a suggestion how to fix this?

Solution: (edit: calledMethodeName has to be a List, otherwise nested methods wouldn't work)

So thanks to Jeff.

When i design the Dynamic like that:

List<string> calledMethodeNames = new List<string>();

public override bool TryGetMember(GetMemberBinder binder,
                                  out object result)
{
    calledMethodeNames.Add(binder.Name);
    result = this;
    return true;
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
    //calledMethodeNames last Element has stored the Name of the called methode (remeber to remove it)
    result = "TryInvoke";
    return true;
}

everything is working fine.

The clue is to return the Object itself as Member, than the object is callable and python calls the TryInvoke (not TryInvokeMember)

But TryInvoke ist called because after returning the object, the object itself is called. So InvokeBinder dont knows the Name of the called methode. So i store it into a variable.


Solution

  • That's the expected behaviour. IronPython uses TryGetMember followed by TryInvoke since the Python language has no concept of invoking a member: Python method calls are always an attribute lookup followed by a call.

    The error you're getting is because you're setting result to a string (type str) and strings are not callable. You'll have to set result to either another dynamic object that implements TryInvoke or a delegate.