سؤال

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.

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

Based on the Python exception (str is not callable) it looks like IronPython first calls TryGetMember and since that function returns true it will use that object.

The TryGetMember/TryInvokeMember should only return true if they succeeded. For example that means TryGetMember should return true if and only if there actually is a member that matches the binder parameter.

Update: in C# Call(2) is always a method call (I can't think of anything else it could be, please let me know if I'm wrong), so the C# compiler will use TryInvokeMethod. In Python however any object is callable if it has a __call__ method, so Call(2) could either mean execute the method Call or get the member Call and execute __call__ on it.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top