Embedding Python in C#

Disclaimer: I don’t know C#. Take any line of code you see below with a grain of salt.

I have developed a USB based device. Now I have to provide a driver for it to the client (actually another developer team). Device is actually based on USB UART but we have a slightly complex protocol running over it, so I had to develop a library for interfacing. I did create one in Python. It wasn’t terribly hard, thanks to an awesome python library.  But the client team work in C#. I couldn’t find a similar library for C#, and we are short on time. I know idea of embedding a binary parser/serializer written in Python into a C# library sounds awful but this is a prototype anyway so…

This is my story of embedding Python into a C# application, using Mono. I got confirmation from a friend that this also works on Visual Studio based projects.

First thing first, we are not embedding the regular Python implementation called CPython into C#. Instead we will use another Python implementation called IronPython. This python implementation is identical to the original one from a language perspective. But its designed to be integrated into the .NET platform. It can make use of .net libraries but there are also disadvantages. For example you can’t use a python library if it makes use of C libraries. Also there is only 2.7 version. If your python code makes use of Python 3 only modules or modules that have C library dependencies you better start looking alternatives to them.

Everything you need is included in the IronPython release package. Download it from here: http://ironpython.net/download/ and extract/install to your preferred location.

Create a new C# project. For this example I’m creating a console based project. Now add IronPython DLLs to your project. You can find these in the IronPython installation folder.

  • IronPython.dll
  • Microsoft.Scripting.dll

You will also need to add Microsoft.CSharp DLL to you project as well. In MonoDevelop this can be done from “References” window, “All” tab. I’m guessing its similar in Visual Studio (maybe not even required).

And here is a simple test code running python:

using System;
using IronPython.Hosting;
using IronPython.Runtime;
using IronPython;
using Microsoft.Scripting.Hosting;

namespace blog
{
    class MainClass
    {
        public static void Main (string[] args)
        {
            ScriptEngine engine = Python.CreateEngine ();

            var result = engine.Execute ("2+2");

            Console.WriteLine (result);
        }
    }
}

This should be fairly simple to understand. Using Python.CreateEngine we are creating an instance of Python interpreter to run our code. And using Execute() method we are running a piece of python code and assigning its result into a C# variable.

Now lets do something a little more useful. We will run a script, and access variables from inside this script. For this we will need to use a ScriptScope.

using System;
using IronPython.Hosting;
using IronPython.Runtime;
using IronPython;
using Microsoft.Scripting.Hosting;

namespace blog
{
    class MainClass
    {
        const string program = @"
a = 3
b = 4
a = b*2
";
        public static void Main (string[] args)
        {
            ScriptEngine engine = Python.CreateEngine ();

            // create a ScriptSource to encapsulate our program and a scope to run it
            ScriptSource source = engine.CreateScriptSourceFromString (program);
            ScriptScope scope = engine.CreateScope ();

            // Execute the script in 'scope'
            source.Execute (scope);

            // access the variables from the 'scope'
            var varA = scope.GetVariable("a");
            var varB = scope.GetVariable("b");

            Console.WriteLine ("a: {0}, b: {1}", varA, varB);
        }
    }
}

Now lets try calling a python function from C#. It’s surprisingly easy.

using System;
using IronPython.Hosting;
using IronPython.Runtime;
using IronPython;
using Microsoft.Scripting.Hosting;

namespace blog
{
    class MainClass
    {
        const string program = @"
def sum(a, b):
    return a+b
";
        public static void Main (string[] args)
        {
            ScriptEngine engine = Python.CreateEngine ();

            ScriptSource source = engine.CreateScriptSourceFromString (program);
            ScriptScope scope = engine.CreateScope ();

            source.Execute (scope);

            // get the function from the python side (remember functions are
            // first class objects in python, you can refer to them like they 
            // are variables)
            var sumFunc = scope.GetVariable("sum");

            var result = sumFunc (2, 2);

            Console.WriteLine ("result: {0}", result);
        }
    }
}

Now lets import some python modules in our script. You will see that any module other than sys cannot be imported in python. That’s because IronPython instance that we embedded inside our application doesn’t know where its standard libraries are. But they are right next to DLLs inside the directory named “Lib/” you will say.. Still, it doesn’t know about them. You have two options; either move those libraries into your applications running directory (not adviced), or let know IronPython where they are.

Important note: before running below example add IronPython.Modules.dll to your project as well.

using System;
using IronPython.Hosting;
using IronPython.Runtime;
using IronPython;
using Microsoft.Scripting.Hosting;

namespace blog
{
    class MainClass
    {
        const string program = @"
import os
print('running in %s' % os.getcwd())
";
        public static void Main (string[] args)
        {
            ScriptEngine engine = Python.CreateEngine ();
            var paths = engine.GetSearchPaths ();
            paths.Add("/home/heyyo/Apps/IronPython-2.7.6.3/Lib"); // change this path according to your IronPython installation
            engine.SetSearchPaths (paths);

            ScriptSource source = engine.CreateScriptSourceFromString (program);
            ScriptScope scope = engine.CreateScope ();

            source.Execute (scope);
        }
    }
}

If you need other modules for your script you should add their paths too using paths.Add().

That’s all for now. I plan to update this post as I discover more stuff in my adventure into the C# land!

One thought on “Embedding Python in C#

Leave a Reply

Your email address will not be published. Required fields are marked *