Embedding IronPython in Silverlight

Using IronPython with C#

Visual Studio Express

 

 

Note

This is part of a series of articles on embedding IronPython:

For a much more in depth introduction to hosting and interacting with dynamic languages from .NET applications see chapters 14 & 15 if IronPython in Action.

Although the basic technique shown here is fine, this example is well out of date and may not work with more recent versions of Silverlight. If you exchange the IronPython binaries for IronPython 2.6 / 2.7 it may still work...

Embedding IronPython in Silverlight

IronPython code is evaluated and executed at runtime. This opens up all sorts of possibilities for user scripting of Silverlight applications.

The IronPython 2 API has been written to make it easy to embed in .NET applications. However, there is a slight difference when embedding IronPython in a Silverlight application. We need to create a runtime using the Silverlight BrowserScriptHost; from this we can create a ScriptEngine that will work with Silverlight (specifically it redirects file reads like imports to the read-only filesystem inside the XAP):

ScriptRuntimeSetup setup = Python.CreateRuntimeSetup(null);
setup.HostType = typeof(Microsoft.Scripting.Silverlight.BrowserScriptHost);
ScriptRuntime runtime = new ScriptRuntime(setup);
ScriptEngine pe = Python.GetEngine(runtime);

We also need to add references to the standard assemblies so that the IronPython code doesn't need to manually call clr.AddReference:

// load platform assemblies so you don't need to call LoadAssembly in script code.
foreach (string name in new string[] { "mscorlib", "System", "System.Windows",
                                       "System.Windows.Browser", "System.Net" })
{
    runtime.LoadAssembly(runtime.Host.PlatformAdaptationLayer.LoadAssembly(name));
}

Note

This example was built with Visual Studio 2008, using the Silverlight tools for Visual Studio and the IronPython for Silverlight assemblies included in the Silverlight Dynamic Languages SDK.

Thanks to Richard Klafter for working out some of the differences when embedding IronPython in Silverlight applications.

If the IronPython code needs to do any imports from other Python files (contained in the xap file) then you need to setup the engine search path. All it needs is an empty string, which means "look in the xap file":

setup.Options["SearchPaths"] = new string[] { string.Empty };

After this the hosting environment in Silverlight is fundamentally the same as in a standard .NET application, so you use the normal hosting API for creating execution scopes and executing code. A more complete sample looks like this:

using IronPython.Hosting;
using Microsoft.Scripting;

...

string code = @"
def function(string):
    return string.upper()";


ScriptRuntimeSetup setup = Python.CreateRuntimeSetup(null);
setup.HostType = typeof(Microsoft.Scripting.Silverlight.BrowserScriptHost);
setup.Options["SearchPaths"] = new string[] { string.Empty };
ScriptRuntime runtime = new ScriptRuntime(setup);
ScriptEngine pe = Python.GetEngine(runtime);

// load platform assemblies so you don't need to call LoadAssembly in script code.
foreach (string name in new string[] { "mscorlib", "System", "System.Windows",
                                       "System.Windows.Browser", "System.Net" })
{
    runtime.LoadAssembly(runtime.Host.PlatformAdaptationLayer.LoadAssembly(name));
}


ScriptScope scope = pe.CreateScope();
ScriptSource source = pe.CreateScriptSourceFromString(code, SourceCodeKind.Statements);
source.Execute(scope);

Func<string, string> func = scope.GetVariable<Func<string, string>>("function");

string result = func("hello world!");

This code creates a Python engine and execution scope. The Python code is executed in the scope and defines a function called function. This function takes a string and returns an uppercase version (by calling the string method upper). After execution this function is pulled out of the scope as a Func delegate.

The full C# code, for Page.xaml.cs, is below. You can also download it as a Visual Studio 2008 project: EmbeddingIronPythonSilverlight Project

using System;
using System.Windows.Controls;
using IronPython;
using IronPython.Hosting;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

namespace EmbeddingIronPythonSilverlight
{

    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            string code = @"
def function(string):
    return string.upper()";

            ScriptRuntimeSetup setup = Python.CreateRuntimeSetup(null);
            setup.HostType = typeof(Microsoft.Scripting.Silverlight.BrowserScriptHost);
            setup.Options["SearchPaths"] = new string[] { string.Empty };

            ScriptRuntime runtime = new ScriptRuntime(setup);
            ScriptEngine pe = Python.GetEngine(runtime);

            // load platform assemblies so you don't need to call LoadAssembly in script code.
            foreach (string name in new string[] { "mscorlib", "System", "System.Windows",
                                                   "System.Windows.Browser", "System.Net" })
            {
                runtime.LoadAssembly(runtime.Host.PlatformAdaptationLayer.LoadAssembly(name));
            }


            ScriptScope scope = pe.CreateScope();
            ScriptSource source = pe.CreateScriptSourceFromString(code, SourceCodeKind.Statements);
            source.Execute(scope);

            Func<string, string> func = scope.GetVariable<Func<string, string>>("function");

            string result = func("hello world!");
            textblock.Text = result;
        }
    }
}

The textblock member is defined in the XAML of the Silverlight application. It has some default text that is overridden with the result of calling our function.

And the result? Shown here:

A string made uppercase by a Python function

There is one more trick to getting this to work. If you recreate this project yourself, adding references to the IronPython assemblies, you will get an error when you try to build the project. The error will be the compiler complaining that the System.Func delegate is defined in both System.Core.dll and in Microsoft.Scripting.Core.dll.

One way round this is to declare your own delegate and use it instead of Func. (In this case the delegate would be public delegate string Function(string input).)

This thread on the IronPython mailing list explains why this problem occurs. You can solve it by aliasing the Microsoft.Scripting.Core.dll. Select it from the references pane in Visual Studio. Change the 'Aliases' property in the 'Reference Properties' pane from 'global' to 'MSCore'. The compiler is no longer confused!

There are some other (perhaps more useful) ideas for embedding IronPython 2 in C# applications in DLR Hosting: Embedding IronPython. For more examples of ways to use the IronPython 2 API, check out the IronPython Cookbook.

The final article in this series is about using Silverlight to put an interactive Python interpreter in the browser:

For buying techie books, science fiction, computer hardware or the latest gadgets: visit The Voidspace Amazon Store.

Hosted by Webfaction

Return to Top

Page rendered with rest2web the Site Builder

Last edited Sat Aug 14 14:32:14 2010.

Counter...