I’m currently working on a project with requires scripts to execute within the .NET environment. After choosing IronPython as the scripting language, I began scouring the web for examples… Here is the code I cobbled together!

[edit] I’ve updated the source code to allow instantiating a class with constructor arguments. [/edit]

The code here demonstrates recipes for 3 types of interop:

  1. Create a dynamic instance of a script and call a method.
  2. Create a dynamic instance of a Python class and call a method.
  3. Imlement a .NET interface in Python and call it from C#.

This example is split between a helper class and a test class:

public class PythonScript
{
    private string _script;
    private readonly ScriptEngine _engine = Python.CreateEngine();

    public PythonScript() { }

    public PythonScript(string script)
    {
        _script = script;
        _engine = Python.CreateEngine();
    }

    public PythonScript(params string[] script)
        : this(CreateScriptString(script))
    {
    }

    private static string CreateScriptString(string[] script)
    {
        StringBuilder scriptBuilder = new StringBuilder();
        foreach (string line in script)
        {
            scriptBuilder.AppendLine(line);
        }
        return scriptBuilder.ToString();
    }

    public string Script
    {
        get { return _script; }
        set { _script = value; }
    }

    public dynamic CreateScriptObject()
    {
        dynamic scriptScope = _engine.CreateScope();

        ScriptSource scriptSource = _engine.CreateScriptSourceFromString(_script, SourceCodeKind.Statements);
        scriptSource.Execute(scriptScope);

        return scriptScope;
    }

    public T CreateInstance<T>(string nameOfClass)
    {
        return CreateInstance(nameOfClass);
    }

    public dynamic CreateInstance(string nameOfClass)
    {
        ScriptScope scriptScope = _engine.CreateScope();

        ScriptSource scriptSource = _engine.CreateScriptSourceFromString(_script, SourceCodeKind.Statements);
        scriptSource.Execute(scriptScope);

        dynamic pythonClass = scriptScope.GetVariable(nameOfClass);

        return pythonClass();
    }
}

 
[TestFixture]
public class PythonScriptTests
{
    [Test]
    public void should_get_hello_message_from_scripted_python_method()
    {
        PythonScript script = new PythonScript(
            "def say_hello():",
            "    return 'Hello from Python!'"
            );

        dynamic helloScript = script.CreateScriptObject();
        string helloMessage = helloScript.say_hello();

        Assert.AreEqual("Hello from Python!", helloMessage);
    }

    [Test]
    public void should_get_hello_message_from_python_class()
    {
        PythonScript script = new PythonScript(
            "class HelloClass:",
            "    def say_hello(self):",
            "        return 'Hello from Python!'"
            );

        dynamic helloClass = script.CreateInstance("HelloClass");
        string helloMessage = helloClass.say_hello();

        Assert.AreEqual("Hello from Python!", helloMessage);
    }

    [Test]
    public void should_get_hello_message_from_python_class_implementing_a_dot_net_interface()
    {
        PythonScript script = new PythonScript(
            "import clr",
            "clr.AddReference('R22.Tests')",
            "from R22.Tests import ISayHello",
            "class HelloClass (ISayHello):",
            "    def SayHello(self):",
            "        return 'Hello from Python!'"
            );

        ISayHello helloClass = script.CreateInstance<ISayHello>("HelloClass");
        string helloMessage = helloClass.SayHello();

        Assert.AreEqual("Hello from Python!", helloMessage);
    }
}

public interface ISayHello
{
    string SayHello();
}

Please note that you will need to reference the IronPython and Microsoft.Scripting dlls in order to run this code. For a working example, please refer to this VS2010 solution.

Hope you find this useful. Happy coding!

Advertisement