Embedding IronPython in C# and Silverlight

Dec 7, 2008 at 2:24 PM
Edited Dec 7, 2008 at 2:25 PM
I've found a couple of examples of how to embed IronPython in C# when using Silverlight 2 but none of them answers my question. I have found samples were simple expressions are executed correctly through IronPython.

In the example below, if I put for example "2+2" in the CreateScriptSourceFromString - method I get out '4' if I execute the code. BUT, why can't I put in for example "print 'test'" in the CreateScriptSourceFromString and then get out "test" on the screen???

            ScriptRuntime _runtime = Python.CreateRuntime();
            ScriptEngine _engine = _runtime.GetEngine("py");
            ScriptScope _scope = _engine.CreateScope();
            ScriptSource _code = _engine.CreateScriptSourceFromString("print 'test'");

            TextBlock1.Text = _code.Execute().ToString();

Thanks for any help!
Coordinator
Dec 11, 2008 at 5:25 AM
Edited Dec 11, 2008 at 6:11 AM
[edit] Codeplex ripped out indentiation from the initial post for some reason, and that's useless for Python code =P [/edit]

"print" is a method in IronPython that displays the string to whatever sys.stdout is set to. sdlsdk doesn't have a default place to print stuff to (like a console, but that's coming in the public tree: http://github.com/jschementi/agdlr), but you can easily tell Python where print should go by setting sys.stdout equal to some class that has a "write" method:

<!-- app.xaml -->
<UserControl x:Class="System.Windows.Controls.UserControl"
    xmlns="http://schemas.microsoft.com/client/2007"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid x:Name="layout_root" Background="White">
        <TextBlock x:Name="message" FontSize="30" />
    </Grid>
</UserControl>

# app.py
from System.Windows import Application
from System.Windows.Controls import UserControl
import sys

class Console(object):
    def __init__(self, element):
        self.element = element
    def write(self, text):
        self.element.Text = self.element.Text + text

root = Application.Current.LoadRootVisual(UserControl(), "app.xaml")
sys.stdout = Console(root.message)

print "Welcome to Python and Silverlight!"
print "More printing!"

I'm making a console in the latest bits, and stdout/stderr for any language will be directed to that console. But to have print to anything different you'll have to do the above.
Dec 11, 2008 at 10:23 AM
Ok, thanks for your answer! But what if I want to use that solution in my above example? Should I create a string containing the whole .py-file and then send it into the CreateScriptFromSource-method?

Actually, I have one more question :-)
I tried to get the C#-hosting example working (the snippet found under "Hosting the DLR from C# or VB"), but I can't get it to work. This is what I got:

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

namespace SilverlightApplication6
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();

            ScriptRuntimeSetup setup = Configuration.LoadFromAssemblies(Package.GetManifestAssemblies());
            setup.HostType = typeof(BrowserScriptHost);

            // 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(BrowserPAL.PAL.LoadAssembly(name));
            }

            // now the desktop and Silverlight are the same ...
            ScriptEngine engine = _runtime.GetEngine("Python");
            ScriptSource source = engine.CreateScriptSourceFromString("System.Windows.Browser.HtmlPage.Document.Message.innerHTML = 'Hello, World'");
            source.Compile().Execute();
        }
    }
}


First problem: There is no runtime declared. How do I declare and Initialize a runtime if I want to run Python?

Second problem: BrowserPAL is not found. It's supposed to be in Microsoft.Scripting.Silverlight but it is not.

Can you please let me know how to change this to a working example? Hopefully then I can get going on my own for a while. (I use sdlsdk 0.4.0)

Thanks for help!


Coordinator
Dec 11, 2008 at 4:00 PM
Yes, just execute the Class definition and sys.stdout = Console() call in python for now, there's an equivalent hosting call but I'm not sure what it is at the moment.

I've updated that example to work now, sorry about that. You can create your own runtime, and you can access the current PAL from that runtime.
Dec 11, 2008 at 5:06 PM
Once again, thanks for your reply. I tried the example you updated under "Hosting the DLR from C# or VB". It compiles fine but when I try to run it I get a SyntaxErrorException: "Unexpected token '='
It is the  source.Compile().Execute();  that throws it. Why is that?

Thanks again!
Coordinator
Dec 11, 2008 at 5:22 PM
Is this the string you're sending to CreateScriptSourceFromString?

"System.Windows.Browser.HtmlPage.Document.Message.innerHTML = 'Hello, World'"

... really, anything could go there, but this is my silly example of doing a Hello world with the browser. Make sure you HTML page has this on it:




~js
Dec 11, 2008 at 5:46 PM
Yes, I tried the "System.Windows.Browser.HtmlPage.Document.Message.innerHTML = 'Hello, World'" string and that didn't work. How can I see if my HTML-page has ~js? What do you mean by that?

Anyway, even if I try this: ScriptSource source = engine.CreateScriptSourceFromString("a = 3");
I still get SyntaxErrorException and Invalid token '='

I know I don't know much about Python but shouldn't the below expression define a method named test?
ScriptSource source = engine.CreateScriptSourceFromString("def test():");
This also gives me SyntaxError and unexpected token 'def'

Below is the source I use:

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


namespace SilverlightApplication6
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();

            ScriptRuntimeSetup setup = Configuration.LoadFromAssemblies(Package.GetManifestAssemblies());
            setup.HostType = typeof(BrowserScriptHost);
            ScriptRuntime runtime = new ScriptRuntime(setup);
            // 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.Host.PlatformAdaptationLayer.LoadAssembly(name);
            }

            // now the desktop and Silverlight are the same ...
            ScriptEngine engine = runtime.GetEngine("Python");
            ScriptSource source = engine.CreateScriptSourceFromString("def test():");
            
            source.Compile().Execute(); //Throws SyntaxErrorException
        }
    }
}


Any idea? Can you run the example?

Thanks!


Coordinator
Mar 3, 2009 at 5:57 PM

You need to pass "def test(): pass" to CreateScriptSourceFromString ... "def test():" is a syntax error in Python.

"System.Windows.Browser.HtmlPage.Document.Message.innerHTML = 'Hello, World'" will give you an error because Python doesn't know what "System" is, you'll need to do "import System" before making that call. Other than that your hosting code looks good.