A certain engineer "COMPLEX"

.NETでPythonを試してみる 第4回

前回はパッケージのインストールに失敗しました。

謝罪


まずはじめに。
IronPython の使用を諦めることにしました。
理由はモジュールのインストールができなかったことです。
一番シンプルだと思っていた simplejson すらまともにできませんでした。
正確には、インストールはできましたが、いざ import simplejson すると、動かない。
他にも、

  • CPythonと同じ方法でC言語ベースのライブラリと相互運用ができない
  • 2017/03/11時点で、Issueの数が700越え。あまりにも利用者の声に応える余裕がない。

結局、ネット上で調べると、動かないという情報ばかり、でこれ以上貴重な時間を費やすことはできない、という判断に至りました。
4日以上使いましたが、本当に無駄骨に終わりました。

Introduction


なので、方針を変えます。
「.NETからPythonを使う」という目的を達するなら、思いつく限り、

  • Pythonをコマンドプロンプトで呼び出し、標準出力で結果を受け取り解析
  • Pythonをコマンドプロンプトで呼び出し、ファイルで結果を受け取り解析
  • Pythonで動作しているWebサービスのAPIを叩く

あたりでしょうか。
1,2番目は面倒ですし、なによりださいので、3番目を利用します。

Webサービスなら、WCFによって簡単に.NETから呼び出せます。
というのも、Pythonをコマンドプロンプトで呼ぶと、呼び出しのコストが大きいと思います。
試してみた限り、Pythonの起動は遅いです。おそらく必要なモジュールなどを読み込んでいるからだと思うのですが、無視できない間隔で待たされます。
なので、起動コストを無視するために、常に起動しっぱなしにして、必要なときに必要な演算結果を受け取る形式がベスト。

ですので、今回はPythonでシンプルなRESTfulサービスを作成し、C#アプリからアクセスします。
そのために軽量なWebフレームワークであるPythonモジュール Flask を使用します。
Flaskについては下記を。

また、使用するPythonはMinicondaを使用します。

今回のソースは下記になります

Sample source code for Demonstration, Experiment and Test - takuya-takeuchi/Demo

Python


Flaskインストール

さくっとインストールします。
下記に従うだけです。


$ python -m pip install flask-restful
Collecting flask-restful
Downloading Flask_RESTful-0.3.5-py2.py3-none-any.whl
Collecting aniso8601>=0.82 (from flask-restful)
Downloading aniso8601-1.2.0.tar.gz (59kB)
100% |################################| 61kB 503kB/s
Requirement already satisfied: pytz in c:\program files\miniconda2\lib\site-packages (from flask-restful)
Requirement already satisfied: six>=1.3.0 in c:\program files\miniconda2\lib\site-packages (from flask-restful)
Collecting Flask>=0.8 (from flask-restful)
Downloading Flask-0.12-py2.py3-none-any.whl (82kB)
100% |################################| 92kB 1.1MB/s
Requirement already satisfied: python-dateutil in c:\program files\miniconda2\lib\site-packages (from aniso8601>=0.82->flask-restful)
Collecting Jinja2>=2.4 (from Flask>=0.8->flask-restful)
Downloading Jinja2-2.9.5-py2.py3-none-any.whl (340kB)
100% |################################| 348kB 1.5MB/s
Collecting Werkzeug>=0.7 (from Flask>=0.8->flask-restful)
Downloading Werkzeug-0.12-py2.py3-none-any.whl (312kB)
100% |################################| 317kB 1.6MB/s
Collecting click>=2.0 (from Flask>=0.8->flask-restful)
Downloading click-6.7-py2.py3-none-any.whl (71kB)
100% |################################| 71kB 2.3MB/s
Collecting itsdangerous>=0.21 (from Flask>=0.8->flask-restful)
Downloading itsdangerous-0.24.tar.gz (46kB)
100% |################################| 51kB 2.3MB/s
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask>=0.8->flask-restful)
Downloading MarkupSafe-1.0.tar.gz
Building wheels for collected packages: aniso8601, itsdangerous, MarkupSafe
Running setup.py bdist_wheel for aniso8601 ... done
Stored in directory: C:\Users\TAKUYA\AppData\Local\pip\Cache\wheels\9e\75\c1\aa2de49c0d1ade4893e73009c0b7792ce89bc6c903a31f854b
Running setup.py bdist_wheel for itsdangerous ... done
Stored in directory: C:\Users\TAKUYA\AppData\Local\pip\Cache\wheels\fc\a8\66\24d655233c757e178d45dea2de22a04c6d92766abfb741129a
Running setup.py bdist_wheel for MarkupSafe ... done
Stored in directory: C:\Users\TAKUYA\AppData\Local\pip\Cache\wheels\88\a7\30\e39a54a87bcbe25308fa3ca64e8ddc75d9b3e5afa21ee32d57
Successfully built aniso8601 itsdangerous MarkupSafe
Installing collected packages: aniso8601, MarkupSafe, Jinja2, Werkzeug, click, itsdangerous, Flask, flask-restful
Successfully installed Flask-0.12 Jinja2-2.9.5 MarkupSafe-1.0 Werkzeug-0.12 aniso8601-1.2.0 click-6.7 flask-restful-0.3.5 itsdangerous-0.24

以上でインストールは完了です。

動作確認

最小のRESTfulなアプリが下記ドキュメントにあるのでファイルに保存します。


from flask import Flask
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

class HelloWorld(Resource):
def get(self):
return {'hello': 'world'}

api.add_resource(HelloWorld, '/')

if __name__ == '__main__':
app.run(debug=True)

以上のソースを下記で起動します。


$ python Python.py
* Restarting with stat
* Debugger is active!
* Debugger PIN: 177-715-949
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

Webブラウザで http://127.0.0.1:5000/ へアクセスすると、下記が表示されます。


{
"hello": "world"
}

jsonで返ってきています。
以上でFlaskのテストは完了です。

C#


SOAPであるならば、エンドポイントを参照して、クライアントクラス等の生成が自動でできますが、RESTにそんな便利機能はありません。

Microsoft.Net.Http

RESTful APIへのアクセスです。RESTful APIへのアクセスはライブラリを使って簡略化します。
幸いMicrosoft.Net.HttpというライブラリがあるのでNuGetで導入します。

Overview This page is about the Microsoft.Net.Http package. Our first release announcement can be found here.   Release Notes Microsoft HTTP Client Libr...

Newtonsoft.Json

RESTful APIから返ってきたjsonの逆シリアライズをNewtonsoft.Jsonで実行します。
こちらもNuGetで導入します。

ソース

WPFです。Xaml側は省略します。
Xamlは、結果を表示するためのメッセージボックスを呼び出すButtonだけです。
ViewModelもButtonに対応するコマンドだけです。

まずは、先ほどのjsonに対するModelクラスです。


using Newtonsoft.Json;

namespace WPFPython.Models
{

[JsonObject("message")]
public sealed class MessageModel
{

[JsonProperty("hello")]
public string Hello { get; set; }

}
}

次はViewModelです。


using System;
using System.Collections.ObjectModel;
using System.Net.Http;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using WPFPython.Models;
using WPFPython.ViewModels.Interfaces;

namespace WPFPython.ViewModels
{

public sealed class MainViewModel : ViewModelBase, IMainViewModel
{

#region フィールド

private readonly IMessageDialog _MessageDialog;

#endregion

#region コンストラクタ

public MainViewModel(IMessageDialog messageDialog)
{
if (messageDialog == null)
throw new ArgumentNullException(nameof(messageDialog));

this._MessageDialog = messageDialog;
}

#endregion

#region プロパティ

private RelayCommand _MessageCommand;

public RelayCommand MessageCommand
{
get
{
return this._MessageCommand ?? new RelayCommand(async () =>
{
var httpClient = new HttpClient();
var stringAsync = httpClient.GetStringAsync("http://127.0.0.1:5000/");
string result = await stringAsync;

var messageModel = Newtonsoft.Json.JsonConvert.DeserializeObject<MessageModel>(result);
this._MessageDialog.ShowMessage(messageModel.Hello);
});
}
}

#endregion

}
}

テスト


実際に実行してみます。
C#側を起動する前に、PythonでRESTサービス側を起動しておいてください。
ボタンを押下すると、REST APIのjsonを受け取り、そのjson唯一のメンバーである、Helloキーに対応する値をメッセージボックスで表示します。

Conclusion


IronPythonが期待外れに終わった今、Pythonの既存資産を生かす方法として、Webサービス化は有用な手段です。
単純に結果を呼び出すだけならRESTful APIは簡単で魅力的です。

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/WPF.Python4

コメントを残す

メールアドレスが公開されることはありません。

%d人のブロガーが「いいね」をつけました。