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:
- Create a dynamic instance of a script and call a method.
- Create a dynamic instance of a Python class and call a method.
- 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!
