Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build Python scripts and call methods from C#

Is there any way to make this scenario work?

There is a Python script. It is built into a DLL by running this script with IronPython:

import clr
clr.CompileModules("CompiledScript.dll", "script.py")

The goal is to call this DLL's methods from C# code. .NET Reflector shows there is one class in the DLL - DLRCashedCode and the methods we are interested in are private static methods of this class.

For example, there is a function in the script:

def scriptMethod(self, text):
...

Its representation in the DLL is:

private static object scriptMethod(Closure closure1, PythonFunction $function, object self, object text)
{
...
}

Closure and PythonFunction are IronPython classes (from Microsoft.Scripting.dll and IronPython.dll).

So far so good. Is it possible this method to be called by C# code? The idea of using reflection like

Type t = typeof(DLRCachedCode);

string methodName = "scriptMethod";
MethodInfo method = t.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);

object[] parameters = new object[] { "param1", "param2" };  // the "params problem"
method.Invoke(null, parameters);

seems harder because of setting the method's parameters. If they are (any how) initialized correctly, could we expect the method to work smoothly?

Is there a better way to call this methods from C#? For various different reasons we prefer to have the script built as a .NET assembly and not to call the script itself.

like image 461
Alex Avatar asked Jan 26 '10 12:01

Alex


2 Answers

Sort of. You cannot access the Python methods directly from C# code. Unless you are playing with C# 4.0 and the dynamic keyword or you are very, very special ;). However, you can compile an IronPython class to a DLL and then use IronPython hosting in C# to access the methods (this is for IronPython 2.6 and .NET 2.0).

Create a C# program like this:

using System;
using System.IO;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
// we get access to Action and Func on .Net 2.0 through Microsoft.Scripting.Utils
using Microsoft.Scripting.Utils;


namespace TestCallIronPython
{
    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            ScriptEngine pyEngine = Python.CreateEngine();

            Assembly myclass = Assembly.LoadFile(Path.GetFullPath("MyClass.dll"));
            pyEngine.Runtime.LoadAssembly(myclass);
            ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");

            // Get the Python Class
            object MyClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));

            // Invoke a method of the class
            pyEngine.Operations.InvokeMember(MyClass, "somemethod", new object[0]);

            // create a callable function to 'somemethod'
            Action SomeMethod2 = pyEngine.Operations.GetMember<Action>(MyClass, "somemethod");
            SomeMethod2();

            // create a callable function to 'isodd'
            Func<int, bool> IsOdd = pyEngine.Operations.GetMember<Func<int, bool>>(MyClass, "isodd");
            Console.WriteLine(IsOdd(1).ToString());
            Console.WriteLine(IsOdd(2).ToString());

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

Make a trivial Python class like this:

class MyClass:
    def __init__(self):
        print "I'm in a compiled class (I hope)"

    def somemethod(self):
        print "in some method"

    def isodd(self, n):
        return 1 == n % 2

Compile it (I use SharpDevelop) but the clr.CompileModules method should also work. Then shove the compiled MyClass.dll into the directory where the compiled C# program lives and run it. You should get this as the result:

Hello World!
I'm in a compiled class (I hope)
in some method
in some method
True
False
Press any key to continue . . .

This incorporates Jeff's more direct solution that eliminates having to create and compile a small Python 'stub' and also shows how you can create C# function calls that access the methods in the Python class.

like image 181
djlawler Avatar answered Sep 19 '22 15:09

djlawler


The clr.CompileModules is purely a load-time optimization - it doesn't make the scripts directly available to a static languge like C#. You'll need to host the IronPython runtime, and then you can load the DLL into the runtime and use IronPython's hosting interfaces to access it.

like image 26
Jeff Hardy Avatar answered Sep 17 '22 15:09

Jeff Hardy