1

spent some time again with the scripting interface of my app. i guess i now have an advanced dlr problem here.

  1. I have a python script
  2. I have an .NET object [o1]
  3. I call a method on the python script from .NET via Iron Python.
  4. The python code creates an object [o2]
  5. The python code calls an method on object [o1] passing [o2] as an argument (Via subclassing DynamicMetaObject)
  6. In the .NET code of the o1-method i want to dynamically call methods on o2
  7. For example i could do that via

    ((dynamic)o2).FuncInPythonScript

so far so good thats all working. .NET calls Python (step 3) Python calls back .NET (step 5)

So i have a basic biderectional control flow between .NET and Python.

We go further:

  1. In the [o1]-method I use LanguageContext.GetMemberNames on [o2]
  2. I wanna call these members somehow via reflection or expressions. Meaning i dont wanna use the dynamic keyword as in step 7. Instead somehow call the methods via reflection. Problem is: a) I do not know how to get the RuntimeType of the Python-Type, meaning i have no System.Reflection.MethodInfo so i stuck here b) I try to use LanguageContext.CreateCallBinder and MetaObject.BindInvokeMember so i should have the method 'FuncInPythonScript' bound But then i'm stuck in how to finally call the bound method.

I see i could use code generation to just generate the code as in step 7, just with the member names from step 8. But is that really necessary?

I do not see wether approach a) or b) might work or maybe there is somthing i did not think of.

Please do not answer with basic "How do i invoke a python method from .NET" hints. That is done in steps 1-7 and i have no problem doing this. It's really an advanced problem.

namespace DynamicMetaObjectTest
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    using System.Dynamic;
    using System.Linq.Expressions;
    using Microsoft.Scripting.Hosting;
    using Microsoft.Scripting.Hosting.Providers;

    class Program
    {
        internal sealed class CDotNetObject : IDynamicMetaObjectProvider
        {

            DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression aExp)
            {
                return new CInvoker(this, aExp);
            }

            private sealed class CInvoker : DynamicMetaObject
            {
                internal CInvoker(CDotNetObject aGws, Expression aExp) : base(aExp, BindingRestrictions.Empty, aGws)
                {
                    this.DotNetObject = aGws;
                }
                private readonly CDotNetObject DotNetObject;

                public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
                {
                    var aMethodInfo = this.GetType().GetMethod("GetSetResultDelegate");
                    var aExp = Expression.Call(Expression.Constant(this), aMethodInfo);
                    var aRestrictions = BindingRestrictions.GetTypeRestriction(this.Expression, this.LimitType);                   
                    var aMetaObject = new DynamicMetaObject(aExp, aRestrictions);
                    return aMetaObject;                   
                }

                public Action<object> GetSetResultDelegate()
                {
                    return this.DotNetObject.SetResultProvider;
                }
            }
            public void SetResultProvider(object aPythonObject_O2)
            {
                var aResult = ((dynamic)aPythonObject_O2).GetResult(); // this is for noobs. ;-)

                var aMetaObjectProvider = (IDynamicMetaObjectProvider)aPythonObject_O2;
                var aMetaObject = aMetaObjectProvider.GetMetaObject(Expression.Constant(aPythonObject_O2));
                var aLanguageContext = HostingHelpers.GetLanguageContext(gScriptEngine);
                var aMemberNames = aLanguageContext.GetMemberNames(aPythonObject_O2);
                var aNonSystemMembers = from aMemberName in aMemberNames where !aMemberName.StartsWith("__") select aMemberName;

                foreach (var aMemberName in aNonSystemMembers)
                {
                    Console.WriteLine("Getting function result from Python script: " + aMemberName);


                    // Now problem:
                    // P1) How to determine wether its an function or an member variable?
                    // P2) How to invoke the method respectively get the value of the member variable?

                    // Your turn ;-)


                    // some of my failures:
                    { // does not work:
                        //var aVar1Binder = aLanguageContext.CreateGetMemberBinder("GetVar1", false);
                        //var aVar1Bound = aMetaObject.BindGetMember(aVar1Binder);
                        //var aCallInfo = new CallInfo(0 , new string[]{});
                        //var aInvokeBinder = aLanguageContext.CreateCallBinder("GetVar1", false, aCallInfo);
                        //var aInvokeBound = aMetaObject.BindInvokeMember(aInvokeBinder, new DynamicMetaObject[]{ aVar1Bound});
                        ////var aInvokeExp = Expression.Invoke(Expression.Constant(aInvokeBound), new Expression[] { });    
                    }
                    { // does not work
                        //var aExpandable = (IronPython.Runtime.Binding.IPythonExpandable)aMetaObject;
                    }
                }
            }           
        }

        static ScriptEngine gScriptEngine;

        static void Main(string[] args)
        {
            var aScriptRuntime = IronPython.Hosting.Python.CreateRuntime();         

            // That's the python script from step 1:
            var aCode = "class CustomView(object) :" + Environment.NewLine +
                        "\tdef GetResult(self) :" + Environment.NewLine +
                        "\t\treturn 42;" + Environment.NewLine +  // cuz 42 is the answer to everything ;-)
                        "DotNetObject.SetResultProvider(CustomView())";

            var aEngine = aScriptRuntime.GetEngine("py");
            gScriptEngine = aEngine;
            var aScope = aEngine.CreateScope();
            var aDotNetObject = new CDotNetObject();
            aScope.SetVariable("DotNetObject", aDotNetObject);

            // That's the invoke to pything from step 3:
            aEngine.Execute(aCode, aScope);
        }
    }
}
charly_b
  • 69
  • 10
  • i thought deeper of generating code... but it seems no good way because every time you run the script an assembly Needs to be loaded. So next question would be if in the meantime .net can UNload assemblies or - alternatively - if it's possible to compile that code without generating an assembly. but i guess not. I saw you can compile Linq-Expressions but for the Linq Expression i Need a method info and that was just one part of the question. – charly_b Nov 09 '15 at 15:15
  • ps2: i know that it must somehow work. Because in the Debugger inspector i see all data of aPythonObject_O2. And i can not imagine they're compiling assemblies for the Debugger view. And another Thing - if it wouldn't work - why is there LanguageContext.GetMemberNames then.... so then will move to another Task now.... Hope one of you guys has an answer. – charly_b Nov 09 '15 at 15:23

0 Answers0