IronPythonをC#アプリに組み込む
最近Pythonがにわかにマイブームで、お仕事でもSQLiteデータベースのメンテやテストデータの生成にPythonを使ったりしています。そんな中、IronPythonをC#アプリ(デスクトップアプリ)に組み込むのを試してみたら思った以上に簡単に出来てほええーとなったのでブログにしたためておいた次第です。
準備
IronPythonのランタイムをプロジェクトに追加しておきます。NuGetからインストールするのが一番楽です。
スクリプトエンジンとスクリプトスコープの作成
ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
スクリプトエンジン (Microsoft.Scripting.Hosting.ScriptEngine) はスクリプトを実行するクラスです。
スクリプトスコープ (Microsoft.Scripting.Hosting.ScriptScope) は変数やクラスの定義情報を格納するクラスです。エンジンにスコープオブジェクトを与えるとスクリプトはそのスコープの中で実行されます。IronPythonで定義した変数やクラスには場合はスコープオブジェクトからアクセスすることができます。
一行ずつ実行
ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
engine.Execute("print 'hello, world');
engine.Execute("foo = 100", scope);
engine.Execute("print foo", scope); // '100'と出力
int bar = engine.Execute<int>("foo", scope); // bar = 100
最後の行のように、式を評価した値を取得することもできます。
コンパイル + 実行
ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
ScriptSource source = engine.CreateScriptSourceFromString(@"
def hello():
print 'hello, world'
hello()
");
CompiledCode code = source.Compile();
try
{
code.Execute(); // 'hello, world'と出力
}
catch (Exception exeption)
{
System.Console.WriteLine("ランタイムエラー: " + exception.Message);
}
文法エラーなどはCompile()メソッドの引数に Microsoft.Scripting.Hosting.ErrorListener
を与えることで取得できます。ランタイムエラーが発生した場合は例外がスローされます。
IronPythonからCLRクラスのメソッドを呼び出す
ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
engine.Execute(@"
import clr
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import MessageBox
MessageBox.Show('hello, world')
"); // => System.Windows.Forms.MessageBoxを表示
C#からIronPythonの関数を呼び出す
ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
engine.Execute(@"
def hello():
print 'hello, world'
", scope);
Action hello = engine.Operations.GetMember<Action>(scope, "hello");
hello(); // => 'hello, world'
C#からIronPythonのクラスのメソッドを呼び出す
ScriptEngine engine = IronPython.Hosting.Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
engine.Execute(@"
class Foo:
def bar(self):
print 'Foo.bar()'
", scope);
object fooObject = engine.Operations.Invoke(scope.GetVariable("Foo"));
Action bar = engine.Operations.GetMember<Action>(fooObject, "hello");
bar(); // => 'Foo.bar()'