2015/04/11

Python バッチファイル一つで Web サーバの起動も再起動もする

[Python][実行環境][BAT]
昨日 wsgiref を使って PostgreSQL に接続する Web サーバ & クライアントを作りましたが、Python スクリプトを修正するたびに Web サーバを再起動しなければいけなくて面倒でした。インタラクティブシェルでなく Windows のバッチファイルを使っていたので余計に。

そこで今後のため、一つのバッチをクリックするだけでサーバの起動も再起動も兼用させ、ついでにコマンドプロンプトの初期状態を最小化(タスクバーに収納)しました。これで再起動時の Ctrl+C 打鍵や「バッチ ジョブを終了しますか」メッセージと無縁になります。実行環境は昨日と同様 Windows 7 32bit + QGIS 2.6 ポータブル版に入っていた Python 2.7。

↓ ファイル構成の例。外部ディスクに入れたポータブル版 QGIS のフォルダ直下に、バッチファイルとフォルダ(Python スクリプトとサーバ用コンテンツ)を入れました。



server.py が Web サーバ本体、ctr_server.py が起動・再起動用に今回加えたスクリプト。処理の流れは次のようになります。

バッチファイル

ctr_server.py(既に Web サーバが起動していれば閉じる)

server.py

HTML など各種の静的コンテンツ


↓ バッチファイルの実質は、単純に Python インタプリタでスクリプトを実行するだけ。コマンドプロンプト最小で起動する方法はこちらそのままです。m(_)m

■ WSGI_Test.bat
@echo off

:: コマンドプロンプト最小で起動
:: http://piyopiyocs.blog115.fc2.com/blog-entry-731.html
::
if not "%HOGE%"=="hoge" (
set HOGE=hoge
start /min cmd /c,"%~0" %*
exit
)

set PYTHONHOME=QGIS/apps/Python27
set PYTHONPATH=QGIS/apps/Python27/Lib
"QGIS/bin/python" python_script/ctr_server.py


バッチから実行されるスクリプト ↓ は How to auto-reload a server on changes with doit : Rounder Wheels を参照し、簡略化したもの。サーバ起動時に必ずプロセス ID ファイルを作成し、再起動時はそこからプロセス ID を読み取って終了させます。

■ ctr_server.py
import os
import signal
import subprocess

fp_current = os.path.abspath(os.path.dirname(__file__))
fp_pid = fp_current + '/pid.txt'
fp_script = fp_current + '/server.py'

def start_server(fp_pid):
# check the server is running
if os.path.exists(fp_pid):
stop_server(fp_pid)

# start server
process = subprocess.Popen(['python', fp_script])

# create pid file
with open(fp_pid, 'w') as pid_file:
pid_file.write(str(process.pid))
return True

def stop_server(fp_pid):
# check server if is running
if not os.path.exists(fp_pid):
return

# try to terminate/stop server's process
with open(fp_pid) as pid_file:
pid = int(pid_file.read())
try:
os.kill(pid, signal.SIGTERM)
except OSError:
pass #ignore errors if process does not exist

# remove pid file
os.unlink(fp_pid)

if __name__ == '__main__':
start_server(fp_pid)


↓ サーバ本体。昨日と同様にリクエスト URI から静的なコンテンツを返します。動作確認だけなら、もっと簡単に make_server と serve_forever だけでいいです。

■ server.py
import os

def app(environ, start_response):
path = environ['PATH_INFO']
if path == '/':
path = '/index.html'
path = os.path.abspath(os.path.dirname(__file__)) + path

if os.path.exists(path):
return static_app(environ, start_response, path)
else:
return show_404_app(environ, start_response)

def static_app(environ, start_response, path):
headers = [('Content-type', content_type(path))]
start_response('200 OK', headers)

with open(path, 'rb') as fh:
content = fh.read()
return content

def show_404_app(environ, start_response):
headers = [('Content-type', 'text/plain')]
start_response('404 Not Found', headers)
return '404 Not Found'

def content_type(path):
MIME_TABLE = {
'.png': 'image/png',
'.html': 'text/html',
'.text': 'text/plain',
'.css': 'text/css',
'.js': 'application/javascript',
}
name, ext = os.path.splitext(path)
if ext in MIME_TABLE:
return MIME_TABLE[ext]
else:
return 'application/octet-stream'

if __name__ == '__main__':
try:
from wsgiref.simple_server import make_server
httpd = make_server('', 8000, app)
print('Serving on port 8000...')
httpd.serve_forever()
except KeyboardInterrupt:
print("\n" + 'Server was stopped.' + "\n")


↓ バッチを実行すると、一瞬コマンドプロンプトが出て最小化し、Web サーバが既存ならそれを閉じて(未存なら何もしない)新しいサーバを起動。スクリプトと同じ場所にプロセス ID ファイルができます。終了時はコマンドプロンプト上で Ctrl+C 一発で閉じます。



↓ テストに使った静的コンテンツ。

■ index.html
<html>
<head>
<style>
body { background:lightblue; color:white; padding:20px }
</style>
</head>
<body>
<div>Hello server on</div>
<img src="//www.python.org/static/img/python-logo.png" width=50% />
</body>
</html>


昨日は静的ファイルの場所を一時的な環境変数で受け渡しましたが、下記を見てスクリプトと同じ場所ならもっと簡単にできると知り、そうしました。

■ PythonRecipe : 134実行しているスクリプトのパスを求める

当初は Windows 側だけで何とかしようと始めたところ、プロセス ID を取得できる形でサーバを起動させることがうまくできず、Python 側での処理に変更。別の機会に役立つかもしれないので、参照したサイトを下記にメモしておきます。

■ VBS for WMI
■ Big Sky : バッチ ジョブを終了しますか (Y/N)?
■ データ連携と統合を科学するブログ : Windowsでコマンドを並列実行する(後編)
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。