無料のMathematicaPlayerを、IronPythonを使ってWeb APIに

[pukiwiki]
[[こちらの記事の続き:http://boxheadroom.com/2009/10/14/mathlink_weatherdata_1]]
無料のMathematicaPlayerをIronPython2.6RC1を使い、Webアプリとして動かしてみました。
こちらの記事を参考にしてさせて頂きました(というか、ほぼ引き写し)  
-[[無料配布MathematicaカーネルとNET Framework実装IronRubyでグリッド・Matheatica計算環境は5分で作れる:http://www.hirax.net/diaryweb/2009/10/06.html#8400]]
-[[WSGIとPythonでスマートなWebアプリケーション開発を 第3回 WSGIミドルウェアの作成:http://gihyo.jp/dev/feature/01/wsgi/0003]]
-[[Python ライブラリリファレンス wsgiref — WSGI ユーティリティとリファレンス実装:http://www.python.jp/doc/2.5/lib/module-wsgiref.html]]
すごく久々にサーバのスクリプトを書きました
[/pukiwiki]

[pukiwiki]
ホントは、何か既存のWebフレームワークを使おうと思ったのですが、頭がこんがらがったので上記の記事を参考にしてテキトー作成。
localhostからのアクセスしか考えてないので、セキュリティ周りははしょってあります。(というか、どうしたら安全なのか判らず)

あくまで自分の勉強用コードということで。。。

*使い方
コマンドプロンプトにて
ipy MathematicaServer.py

として起動。

http://localhost:8000/evaluation/式   
          式をMathematicaPlayerで評価
          プレーンテキストとして結果が返ってきます。
/restart    MathLinkを再起動
/shutdown  サーバを終了

CPythonからWeb API経由で呼び出すためのモジュールを書くとCGソフトのBlenderなどからも使えて便利かも。
(分散処理しないなら子プロセスのほうが簡単だったかしらん?)

例)
http://localhost:8000/evaluate/WeatherData[{“Gifu”,”Japan”},”Temperature”]
14.4 #岐阜の現在気温 14.4℃が返ってきました。

以下コード

[/pukiwiki]

まずは、URLに応じて関数を呼ぶためのディスパッチャ(WSGIのミドルウェア)とユーティリティ関数。
app.pyとして保存。

こちらは、IronPythonでもCPython2.5でも動きました

#-*- coding:utf-8 -*-
"""
app.py  

original source
WSGIとPythonでスマートなWebアプリケーション開発を
第3回 WSGIミドルウェアの作成
http://gihyo.jp/dev/feature/01/wsgi/0003
"""

from wsgiref import util, simple_server
class const(object):
    name = 'SCRIPT_NAME'
    info = 'PATH_INFO'

def header(start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
def notFound(environ, start_response):
    start_response("404 NotFound",[('Content-type', 'text/plain')])
    return '%s is not found' % util.request_uri(environ)

class SelectApp(object):
    u''' パスによるアプリケーション振り分け'''
    def __init__(self,notfound=notFound):
        # 割り振るパスが見つからなかったときに呼び出すアプリケーション
        self.notfound = notfound
        self.table=[]
        self.flag=True
    def dispatch(self,path):
        def _(app):
            self.table.append( (path,app))
            return app       
        return _

    def __call__(self, environ, start_response):
        u''' リクエストのパスを見て振り分ける '''
        scriptname,pathinfo=name_info(environ) 
        for p, app in self.table:
            if p == '' or p == '/' and pathinfo.startswith(p):
                return app(environ, start_response)
            if pathinfo == p or  pathinfo.startswith(p) and \
                    pathinfo[len(p)] == '/':
                fetchone(environ,p)
                #scriptname,pathinfo=name_info(environ)
                return app(environ, start_response)

        return self.notfound(environ, start_response)

def name_info(environ):
    scriptname = environ.get(const.name, '')
    pathinfo = environ.get(const.info, '')
    return scriptname,pathinfo

def fetchone(environ,p=""):
    scriptname,pathinfo=name_info(environ) 
    if not p:
        plist=pathinfo.split("/")
        p=plist.pop(0)
    scriptname+= p
    pathinfo=pathinfo[len(p):]
    environ[const.name] =scriptname
    environ[const.info] = pathinfo
    return  p

こちらはIronPython専用

MathematicaServer.pyとして保存。

#-*- coding:utf-8 -*-
"""
MathematicaServer.py
original source

無料配布MathematicaカーネルとNET Framework実装IronRubyで
グリッド・Matheatica計算環境は5分で作れる
http://www.hirax.net/diaryweb/2009/10/06.html#8400

"""
import clr , System
require=clr.AddReferenceToFile
#require=clr.AddReferenceByName
require("Wolfram.NETLink")
from Wolfram.NETLink import *
import sys
from app import *
from wsgiref import  simple_server


mlpath="-linkmode launch -linkname "
#full path to mathkernel.exe
mlpath+=" 'C:\\Program Files\\Wolfram Research\\Mathematica Player\\7.0\\mathkernel.exe' "

class Mathematica(object):
  def open(self):
    self.kernelLink=MathLinkFactory.CreateKernelLink(mlpath)
    self.kernelLink.WaitAndDiscardAnswer()
  def do(self,command):
    return self.kernelLink.EvaluateToInputForm(command, 0)
  def close(self):
    self.kernelLink.EvaluateToInputForm('MVClose[]', 0)
    self.kernelLink.Close()

app = SelectApp()
dispatch=app.dispatch

@dispatch("/restart")
def restart(environ,start_response):
    header(start_response)
    m.close()
    m.open()
    return "OK"

@dispatch("/evaluate")
def evaluate(environ,start_response):
    scriptname,pathinfo=name_info(environ) 
    if pathinfo :
        ret= m.do(pathinfo[1:])
        header(start_response)
        return ret
    else :
        return notFound(environ,start_response)
@dispatch("/shutdown")
def shutdown(environ,start_response):
    app.flag=False
    header(start_response)
    return "Shutdown"

       

if __name__ == '__main__':
    import threading,time
    
    def watch():
        while app.flag :
            time.sleep(1.)

    m=Mathematica()
    m.open()
    srv = simple_server.make_server('127.0.0.1', 8000, app)
    th=threading.Thread(target=srv.serve_forever)
    th.setDaemon(False)
    w=threading.Thread(target=watch)
    w.setDaemon(True)
    w.start()
    th.start()
    w.join()
    th.join(1.)
    sys.exit()    

shutdownまわりがゴチャゴチャしております。

コメントを残す

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