Ubuntuで作ったwheelパッケージをWindowsにインストールする

このあいだ Ubuntu で作った wheel パッケージ、Windows にインストールしてみた。
pure Python なのであたりまえだけど、何の問題もなくインストールできた。

^o^ > pip install myapp-0.0.1-py2-none-any.whl
Processing c:\users\hiro\documents\tmp\myapp-0.0.1-py2-none-any.whl
Installing collected packages: myapp
Successfully installed myapp-0.0.1

動作確認。

^o^ > myapp foo bar baz
['C:\\Python27\\Scripts\\myapp', 'foo', 'bar', 'baz']

^o^ > which myapp
C:/Python27/Scripts/myapp.EXE

へぇ、Windows だと .EXE ファイルができるんだ。

Pythonのwith構文を覚えた!

基本的な使い方

ファイルを開いて読み込む処理が典型的かな。今まではこうしていた:

f = open('data.txt', 'r')
print f.read()
f.close()

with 構文を使うとこう書ける:

with open('data.txt', 'r') as f:
    print f.read()

開いたファイルは、ブロックを抜けるときに自動で閉じてくれる。

withに対応したクラス

with 構文に書けるクラスはファイルだけじゃない。__enter____exit__ の2つのメソッドを持ったクラス(のインスタンス)なら何でもいい。__enter__ はブロックに入るときに、__exit__ はブロックを抜けるときに呼ばれる。
ちょっと試してみよう。

class Test():
    def __init__(self, hoge):
        print 'init'
        self.hoge = hoge

    def print_hoge(self):
        print self.hoge

    def __enter__(self):
        print 'enter'
        return self

    def __exit__(self, type, value, traceback):
         print 'exit'

with Test('hoge') as t:
    t.print_hoge()

実行結果:

takatoh@apostrophe $ python test_with.py
init
enter
hoge
exit

ブロックの中を実行する前に __enter__ が、あとに __exit__ が呼ばれているのがわかる。
as t: の部分の t には、__enter__ の返り値が代入されるので、self を返している。

ともあれ、1行短くなって見やすくなる。これからはこうやって書くようにしよう。

Python: コマンドを含むwheelパッケージを作る

Python のパッケージというと、以前は Egg だったけど最近では wheel パッケージが増えているようだ。Egg パッケージは一度作ってみたことがあるので、今回は wheel パッケージを作ってみる。ついでに、ライブラリではなく、実行できるコマンドを含んだパッケージを作る。

環境

  • Ubuntu 16.04 LTS
  • Python 2.7.12
  • wheel 0.29.0

ソースファイル

ソースの構成はこう:

takatoh@apostrophe $ tree .
.
├── myapp
│   ├── __init__.py
│   └── main.py
└── setup.py

1 directory, 3 files

myapp/main.py が実行されるコマンドのソースで、setup.py が wheel パッケージを作るためのファイル。

import sys

def main():
    print(sys.argv)
import setuptools

setuptools.setup(
    name='myapp',
    version='0.0.1',
    packages=setuptools.find_packages(),
    entry_points={
        'console_scripts':[
            'myapp=myapp.main:main'
        ]
    }
)

myapp/main.py は main 関数があるだけ。setup.py も最低限だけど、色を付けた行が実行コマンドを指定していて、'myapp=myapp.main:main'myapp コマンドを実行すると myapp/main.py の main 関数を呼び出すことを意味している。myapp/__init__.py は空でいい。

wheelパッケージの作成

setup.py にパラメータとして bdist_wheel を渡してやる。

takatoh@apostrophe $ python setup.py bdist_wheel
running bdist_wheel
running build
running build_py
creating build
creating build/lib.linux-x86_64-2.7
creating build/lib.linux-x86_64-2.7/myapp
copying myapp/main.py -> build/lib.linux-x86_64-2.7/myapp
copying myapp/__init__.py -> build/lib.linux-x86_64-2.7/myapp
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
creating build/bdist.linux-x86_64/wheel/myapp
copying build/lib.linux-x86_64-2.7/myapp/main.py -> build/bdist.linux-x86_64/wheel/myapp
copying build/lib.linux-x86_64-2.7/myapp/__init__.py -> build/bdist.linux-x86_64/wheel/myapp
running install_egg_info
running egg_info
creating myapp.egg-info
writing myapp.egg-info/PKG-INFO
writing top-level names to myapp.egg-info/top_level.txt
writing dependency_links to myapp.egg-info/dependency_links.txt
writing entry points to myapp.egg-info/entry_points.txt
writing manifest file 'myapp.egg-info/SOURCES.txt'
reading manifest file 'myapp.egg-info/SOURCES.txt'
writing manifest file 'myapp.egg-info/SOURCES.txt'
Copying myapp.egg-info to build/bdist.linux-x86_64/wheel/myapp-0.0.1.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/myapp-0.0.1.dist-info/WHEEL

すると dist ディレクトリに wheel パッケージができる。

takatoh@apostrophe $ ls dist
myapp-0.0.1-py2-none-any.whl

インストールと確認

インストールは普通に sudo pip install で。

takatoh@apostrophe $ sudo pip install dist/myapp-0.0.1-py2-none-any.whl
Processing ./dist/myapp-0.0.1-py2-none-any.whl
Installing collected packages: myapp
Successfully installed myapp-0.0.1

テスト。

takatoh@apostrophe $ myapp foo bar baz
['/usr/local/bin/myapp', 'foo', 'bar', 'baz']

うまく行った。myapp コマンドが /use/local/bin/myapp にインストールされているのがわかる。

参考ページ

 cf. Pythonでグローバルコマンドを含んだパッケージを作る – Qiita
 cf. Python: Wheel でパッケージを配布する – CUBE SUGAR STORAGE

Flaskアプリを引っ越す(5)解決編

データの更新ができない問題が解決した。
アプリのせいじゃなくて、パーミッションの問題だった。次のようにして解決。

bruschetta@wplj:~/bruschetta$ chmod 666 bruschetta/bruschetta.db
bruschetta@wplj:~/bruschetta$ chmod 777 bruschetta

最初、データベースファイルのパーミッションだけ変えてみたけどダメで、ディレクトリのパーミッションも変えたら OK になった。

Flaskアプリを引っ越す(3)

昨日は、Ubuntu 起動時にアプリを自動起動する systemctl enable コマンドでエラーが出る、というところで終わった。

動いている

エラーが出ているのでてっきり自動起動に失敗しているものとばかり思っていたけど、ちゃんと enabled になっていた。

bruschetta@wplj:~$ systemctl list-unit-files | grep bruschetta
bruschetta.service                         enabled
bruschetta@wplj:~$ systemctl status bruschetta
● bruschetta.service - Bruschetta service
   Loaded: loaded (/lib/systemd/system/bruschetta.service; enabled; vendor prese
   Active: active (running) since 火 2017-02-28 22:42:15 JST; 6h ago
  Process: 1105 ExecStart=/etc/init.d/bruschetta start (code=exited, status=0/SU
 Main PID: 1138 (uwsgi)
   CGroup: /system.slice/bruschetta.service
           ├─1138 uwsgi bruschetta.ini
           ├─1569 uwsgi bruschetta.ini
           └─1570 uwsgi bruschetta.ini

試しにリブートしてみても、ちゃんと自動起動していることを確認した。
うーん、ますます訳がわかんなくなってきた。が、まあいい、この件は後でゆっくり調べることにして、先に進めよう。

データベースファイルをコピー

apostrophe からデータベースファイルをコピーする。Sqlite3 なので、単にファイルを上書きコピーすればいいだけだ。

bruschetta@wplj:~/bruschetta/bruschetta$ scp bruschetta@apostrophe:bruschetta/bruschetta/bruschetta.db .
bruschetta@apostrophe's password: 
bruschetta.db                                 100%  876KB 876.0KB/s   00:00

そんで、アプリを再起動。

bruschetta@wplj:~/bruschetta/bruschetta$ sudo systemctl stop bruschetta
[sudo] bruschetta のパスワード: 
bruschetta@wplj:~/bruschetta/bruschetta$ sudo systemctl start bruschetta

service コマンドじゃなくて systemctl コマンドを使ってみた。ブラウザで確認してみると、無事データが移行されていた。

Nginxのヴァーチャルホスト

ヴァーチャルホスト bruschetta でアプリにアクセスできるようにする。これも設定ファイルを apostrophe からコピーしてくる。

bruschetta@wplj:~/bruschetta/bruschetta$ cd /etc/nginx/sites-available
bruschetta@wplj:/etc/nginx/sites-available$ sudo scp bruschetta@apostrophe:/etc/nginx/sites-available/bruschetta .
bruschetta@apostrophe's password: 
bruschetta                                    100%  905     0.9KB/s   00:00

コピーした設定ファイルがこれ。

upstream uwsgi-bruschetta {
    server 127.0.0.1:9090;
}

server {
    # port
    listen 80;

    # server name
    server_name bruschetta;

    # document root
    root /home/bruschetta/bruschetta;

    # index
    #index index.php index.html index.htm;

    # log files
    access_log /var/log/nginx/bruschetta/access.log combined;
    error_log /var/log/nginx/bruschetta/error.log warn;

    keepalive_timeout 60;
    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;

    #client_max_body_size 20M;

    location / {
        #root /home/bruschetta/bruschetta;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://uwsgi-bruschetta;
    }

}

sites-enabled/bruschetta にリンクを張る。

bruschetta@wplj:/etc/nginx/sites-available$ sudo ln -s /etc/nginx/sites-available/bruschetta /etc/nginx/sites-enabled/bruschetta
bruschetta@wplj:/etc/nginx/sites-available$ cd ../sites-enabled
bruschetta@wplj:/etc/nginx/sites-enabled$ ls
bruschetta  default

ログファイル用のディレクトリを作る。

bruschetta@wplj:/etc/nginx/sites-enabled$ sudo mkdir /var/log/nginx/bruschetta

/etc/hosts ファイルを編集して1行追加。

192.168.1.6 bruschetta

そして Nginx を再起動。

bruschetta@wplj:/etc/nginx/sites-enabled$ sudo systemctl restart nginx

OK。あとは他のマシンの hosts ファイルも編集して bruschetta にアクセスできるようにするだけだ。はー、長かった。

Flaskアプリを引っ越す(2)

前のエントリは、/lib/systemd/system/bruschetta.service ファイルの書き方がわからない、というところで終わった。

Unit設定ファイル

その後、なんとなくではあるけど書けたので、とりあえずは動くようになった。参考になったページはここ。

 cf. 第4回 Unit設定ファイルの記述方法 – ITPro

Unit設定ファイルというらしい。ともかく、このページを参考にして書いたのがこれ。

[Unit]
Description=Bruschetta service
After=network.target

[Service]
ExecStart=/etc/init.d/bruschetta start
ExecStop=/etc/init.d/bruschetta stop
Restart=always
Type=forking
PIDFile=/var/run/bruschetta.pid

このファイルから、/etc/systemd/system/bruschetta.service にリンクを張る。

bruschetta@wplj:~$ sudo ln -s /lib/systemd/system/bruschetta.service /etc/systemd/system/bruschetta.service

起動確認

で、起動。

bruschetta@wplj:~$ sudo service bruschetta start

やった!動いた!
startstop を繰り返してみると、ちゃんと起動、停止する。今度は大丈夫そうだ。

ところが

Ubuntu 自体を再起動すると、サービスが動いていない。またもググッて調べた結果、systemctl enable コマンドを使えばいいらしい。

 cf. Systemdを使ってさくっと自作コマンドをサービス化してみる – Qiita

bruschetta@wplj:~$ sudo systemctl enable bruschetta
[sudo] bruschetta のパスワード: 
Synchronizing state of bruschetta.service with SysV init with /lib/systemd/systemd-sysv-install...
Executing /lib/systemd/systemd-sysv-install enable bruschetta
insserv: warning: script 'bruschetta' missing LSB tags and overrides
update-rc.d: error: bruschetta Default-Start contains no runlevels, aborting.

あれぇ?

更にググるとこのページを見つけた。

 cf. Systemd入門(4) – serviceタイプUnitの設定ファイル – めもめも

/lib/systemd/system/bruschetta.service ファイルに [Install] セクションを付け足してみる。

[Unit]
Description=Bruschetta service
After=network.target

[Service]
ExecStart=/etc/init.d/bruschetta start
ExecStop=/etc/init.d/bruschetta stop
Restart=always
Type=forking
PIDFile=/var/run/bruschetta.pid

[Install]
WantedBy=multi-user.target

今度はどうだ。

bruschetta@wplj:~$ sudo systemctl enable bruschetta
Synchronizing state of bruschetta.service with SysV init with /lib/systemd/systemd-sysv-install...
Executing /lib/systemd/systemd-sysv-install enable bruschetta
insserv: warning: script 'K01bruschetta' missing LSB tags and overrides
insserv: warning: script 'bruschetta' missing LSB tags and overrides
update-rc.d: error: bruschetta Default-Start contains no runlevels, aborting.

ああダメだ。……というところで今日は時間切れ。

Flaskアプリを引っ越す

apostrophe で動いている Flask アプリを wplj に引っ越す。

virtualenvとuwsgiのインストール

takatoh@wplj $ pip install virtualenv
プログラム 'pip' はまだインストールされていません。 次のように入力することでインストールできます:
sudo apt install python-pip

あれ、pip がインストールされてなかった。

takatoh@wplj $ sudo apt install python-pip

では、あらためて。

takatoh@wplj $ sudo pip install virtualenv
takatoh@wplj $ sudo pip install uwsgi

以前のエントリを見たら sudo をつけていたので、今回もつけて実行した。

takatoh@wplj $ pip list
adium-theme-ubuntu (0.3.4)
dnspython (1.12.0)
pip (8.1.1)
pycrypto (2.6.1)
setuptools (20.7.0)
unity-lens-photos (1.0)
uWSGI (2.0.14)
virtualenv (15.1.0)
wheel (0.29.0)
You are using pip version 8.1.1, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

virtualenv と uWSGI がちゃんとインストールされてる。pip をアップグレードしろみたいなメッセージが出てるけど、これはあとでいいや。

ユーザを作る

Flask アプリを動かす専用のユーザを作る。

takatoh@wplj $ sudo adduser bruschetta
ユーザー `bruschetta' を追加しています...
新しいグループ `bruschetta' (1001) を追加しています...
新しいユーザー `bruschetta' (1001) をグループ `bruschetta' に追加しています...
ホームディレクトリ `/home/bruschetta' を作成しています...
`/etc/skel' からファイルをコピーしています...
新しい UNIX パスワードを入力してください: 
新しい UNIX パスワードを再入力してください: 
passwd: パスワードは正しく更新されました
bruschetta のユーザ情報を変更中
新しい値を入力してください。標準設定値を使うならリターンを押してください
	フルネーム []: 
	部屋番号 []: 
	職場電話番号 []: 
	自宅電話番号 []: 
	その他 []: 
以上で正しいですか? [Y/n] y

sudo する権利をつける。

takatoh@wplj $ sudo gpasswd -a bruschetta sudo
ユーザ bruschetta をグループ sudo に追加

ここで今作ったユーザでログインしなおす。

Flaskアプリの配置

GitHub からクローン。

bruschetta@wplj:~$ git clone https://github.com/takatoh/Bruschetta.git bruschetta
Cloning into 'bruschetta'...
remote: Counting objects: 358, done.
remote: Total 358 (delta 0), reused 0 (delta 0), pack-reused 358
Receiving objects: 100% (358/358), 31.96 KiB | 0 bytes/s, done.
Resolving deltas: 100% (241/241), done.
Checking connectivity... done.

virtualenv を作って必要なライブラリをインストール。

bruschetta@wplj:~/bruschetta$ virtualenv env
New python executable in /home/bruschetta/bruschetta/env/bin/python
Installing setuptools, pip, wheel...done.
bruschetta@wplj:~/bruschetta$ source env/bin/activate
(env) bruschetta@wplj:~/bruschetta$ pip install -r requirements.txt
Collecting Flask (from -r requirements.txt (line 1))
  Downloading Flask-0.12-py2.py3-none-any.whl (82kB)
    100% |████████████████████████████████| 92kB 3.0MB/s 
Collecting Flask-SQLAlchemy (from -r requirements.txt (line 2))
  Downloading Flask_SQLAlchemy-2.2-py2.py3-none-any.whl
Collecting Flask-Script (from -r requirements.txt (line 3))
  Downloading Flask-Script-2.0.5.tar.gz (42kB)
    100% |████████████████████████████████| 51kB 4.0MB/s 
Collecting itsdangerous>=0.21 (from Flask->-r requirements.txt (line 1))
  Downloading itsdangerous-0.24.tar.gz (46kB)
    100% |████████████████████████████████| 51kB 4.6MB/s 
Collecting click>=2.0 (from Flask->-r requirements.txt (line 1))
  Downloading click-6.7-py2.py3-none-any.whl (71kB)
    100% |████████████████████████████████| 71kB 4.4MB/s 
Collecting Werkzeug>=0.7 (from Flask->-r requirements.txt (line 1))
  Downloading Werkzeug-0.11.15-py2.py3-none-any.whl (307kB)
    100% |████████████████████████████████| 317kB 3.0MB/s 
Collecting Jinja2>=2.4 (from Flask->-r requirements.txt (line 1))
  Downloading Jinja2-2.9.5-py2.py3-none-any.whl (340kB)
    100% |████████████████████████████████| 348kB 4.0MB/s 
Collecting SQLAlchemy>=0.8.0 (from Flask-SQLAlchemy->-r requirements.txt (line 2))
  Downloading SQLAlchemy-1.1.5.tar.gz (5.1MB)
    100% |████████████████████████████████| 5.1MB 237kB/s 
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask->-r requirements.txt (line 1))
  Downloading MarkupSafe-0.23.tar.gz
Building wheels for collected packages: Flask-Script, itsdangerous, SQLAlchemy, MarkupSafe
  Running setup.py bdist_wheel for Flask-Script ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/e2/ea/d8/8d114e46cef819f7d9879504a7f9cb2a88a479af2858223d9f
  Running setup.py bdist_wheel for itsdangerous ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/fc/a8/66/24d655233c757e178d45dea2de22a04c6d92766abfb741129a
  Running setup.py bdist_wheel for SQLAlchemy ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/8d/0a/3b/2109101a4052e58ad64f83661383a6b483a1c383eb09aad6d6
  Running setup.py bdist_wheel for MarkupSafe ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/a3/fa/dc/0198eed9ad95489b8a4f45d14dd5d2aee3f8984e46862c5748
Successfully built Flask-Script itsdangerous SQLAlchemy MarkupSafe
Installing collected packages: itsdangerous, click, Werkzeug, MarkupSafe, Jinja2, Flask, SQLAlchemy, Flask-SQLAlchemy, Flask-Script
Successfully installed Flask-0.12 Flask-SQLAlchemy-2.2 Flask-Script-2.0.5 Jinja2-2.9.5 MarkupSafe-0.23 SQLAlchemy-1.1.5 Werkzeug-0.11.15 click-6.7 itsdangerous-0.24

データベースの初期化。

(env) bruschetta@wplj:~/bruschetta$ python manage.py init_db
manage.py:2: ExtDeprecationWarning: Importing flask.ext.script is deprecated, use flask_script instead.
  from flask.ext.script import Manager
/home/bruschetta/bruschetta/bruschetta/__init__.py:2: ExtDeprecationWarning: Importing flask.ext.sqlalchemy is deprecated, use flask_sqlalchemy instead.
  from flask.ext.sqlalchemy import SQLAlchemy

なんか SQLAlchemy がらみでメッセージが出てるな。まあいい、先に進めよう。
起動確認。

(env) bruschetta@wplj:~/bruschetta$ python manage.py runserver
manage.py:2: ExtDeprecationWarning: Importing flask.ext.script is deprecated, use flask_script instead.
  from flask.ext.script import Manager
/home/bruschetta/bruschetta/bruschetta/__init__.py:2: ExtDeprecationWarning: Importing flask.ext.sqlalchemy is deprecated, use flask_sqlalchemy instead.
  from flask.ext.sqlalchemy import SQLAlchemy
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [28/Feb/2017 20:14:23] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [28/Feb/2017 20:14:23] "GET /static/style.css HTTP/1.1" 200 -
127.0.0.1 - - [28/Feb/2017 20:14:23] "GET /favicon.ico HTTP/1.1" 404 -

OK。ブラウザでも確認できた。
ここで virtualenv から抜ける。

(env) bruschetta@wplj:~/bruschetta$ deactivate

uWSGIで起動

uWSGI の設定ファイルを apostrophe からコピーしてくる。

bruschetta@wplj:~/bruschetta$ scp bruschetta@apostrophe:bruschetta/bruschetta.ini .
bruschetta@apostrophe's password: 
bruschetta.ini                                100%  350     0.3KB/s   00:00

内容はこんな感じ。

[uwsgi]
uid = www-data
gid = www-data
http = :9090
venv = /home/bruschetta/bruschetta/env
python-path = /home/bruschetta/bruschetta
wsgi-file = /home/bruschetta/bruschetta/manage.py
callable = app
master=true
pidfile=/var/run/bruschetta.pid
daemonize=/home/bruschetta/bruschetta/bruschetta.log

ディレクトリ構成は一緒だから、このまま行けるはずだ。

bruschetta@wplj:~/bruschetta$ sudo uwsgi bruschetta.ini
sudo bruschetta のパスワード: 
uWSGI getting INI configuration from bruschetta.ini

OK。今度もブラウザで確認できた。

起動スクリプト

これも以前のスクリプトと同じ。

#!/bin/sh

PATH=/usr/local/bin:/usr/bin:/bin:/sbin
APP_ROOT=/home/bruschetta/bruschetta

case "$1" in
start)
cd ${APP_ROOT}
uwsgi bruschetta.ini
;;
stop)
PID_FILE=/var/run/bruschetta.pid
PID=`cat ${PID_FILE}`
kill -INT ${PID}
rm ${PID_FILE}
;;
*)
echo "Usage: bruschetta {start|stop}" >&2
exit 1
;;
esac

exit 0

起動確認。

bruschetta@wplj:~/bruschetta$ sudo service bruschetta start
Failed to start bruschetta.service: Unit bruschetta.service not found.

あれ、ダメだ。bruschetta.service ってなんだ?
ググってみると、/lib/systemd/system/bruschetta.service ファイルを作ってやればいいようだけど、書き方がよくわからない。どうしたもんか……

RubyとPythonをインストール

Ruby

takatoh@wplj $ sudo apt install ruby
takatoh@wplj $ ruby -v
ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]

おお、2.3だ。

Python

takatoh@wplj $ sudo apt install python
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
python はすでに最新バージョン (2.7.11-1) です。
python は手動でインストールしたと設定されました。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 432 個。

あれ、入ってる?

takatoh@wplj $ python -V
Python 2.7.11+

入ってた。

PythonでMD5ダイジェスト値を求める

hashlib を使う。
試しに、MD5 を計算/チェックするスクリプトを書いてみた。

#!/usr/bin/env python
# encoding: utf-8

import argparse
import hashlib

script_version = "0.1.0"

def calc_md5(filename):
    m = hashlib.md5()
    f = open(filename, "rb")
    m.update(f.read())
    f.close
    return m.hexdigest()

def check_md5(filename, md5):
    return md5 == calc_md5(filename)

parser = argparse.ArgumentParser(description="calc/check MD5.")
parser.add_argument("file", metavar="FILE", action="store",
                    help="specify file.")
parser.add_argument("-v", "--version", action="version", version=script_version,
                    help="show version and exit")
parser.add_argument("-c", "--check", dest="check", action="store_true",
                    help="check MD5")
args = parser.parse_args()

if args.check:
    f = open(args.file, "r")
    s = f.read().strip()
    f.close()
    md5, filename = s.split(" ")
    if check_md5(filename, md5):
        print filename + " OK"
    else:
        print filename + " FAILED"
    else:
        filename = args.file
print calc_md5(filename) + " " + filename

実行例:

takatoh@apostrophe $ python md5.py sample.zip > a.md5
takatoh@apostrophe $ cat a.md5
72f163b717dbe53cd58902713dc10152  sample.zip
takatoh@apostrophe $ python md5.py -c a.md5
sample.zip OK

bottleアプリをuWSGIで動かす

やってみよう!

今回動かすアプリ

単純に、指定されたファイルをダウンロードさせるだけのアプリを書いた。

# encoding: utf-8

from bottle import route, static_file, abort, run, default_app
import os

# root

@route('/')
def index():
    abort(404, "File not found.")

# /path/to/file

@route('/')
def static(path):
    file_path = os.path.join("./storage", path)
    if os.path.isfile(file_path):
        return static_file(path, root='./storage', download=True)
    else:
        abort(404, "File not found.")

if __name__ == '__main__':
    run(host='localhost', port='8080', debug=True, reloader=True)
else:
    application = default_app()

run 関数が直接 python index.py したとき用の記述で、application = default_app() が uWSGI 用の記述。

下準備

uWSGI で動かす前に、実行用の環境を env という名前で作っておく。

takatoh@apostrophe $ ls
index.py  requirements.txt
takatoh@apostrophe $ virtualenv env
New python executable in env/bin/python
Installing setuptools, pip, wheel...done.
takatoh@apostrophe $ source env/bin/activate
(env)takatoh@apostrophe $ pip install -r requirements.txt
Collecting bottle==0.12.10 (from -r requirements.txt (line 1))
/home/takatoh/w/myapp/env/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
  Using cached bottle-0.12.10-py2-none-any.whl
Installing collected packages: bottle
Successfully installed bottle-0.12.10
/home/takatoh/w/myapp/env/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
You are using pip version 7.1.2, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
(env)takatoh@apostrophe $ deactivate

それから、storage という名前のディレクトリを作って、ファイル(101_ObjectCategories.tar.gz)を入れておく。これをダウンロードしてみようってわけだ。

uWSGIで起動

まずは、コマンドラインから起動してみる。

takatoh@apostrophe $ uwsgi --http :8080 --wsgi-file index.py -H env

ダウンロードできるか、テスト。

takatoh@apostrophe $ wget http://localhost:8080/101_ObjectCategories.tar.gz
--2016-11-24 20:33:35--  http://localhost:8080/101_ObjectCategories.tar.gz
localhost (localhost) をDNSに問いあわせています... 127.0.0.1
localhost (localhost)|127.0.0.1|:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 131740031 (126M) [application/x-tar]
`101_ObjectCategories.tar.gz' に保存中

100%[======================================>] 131,740,031 68.1MB/s   時間 1.8s 

2016-11-24 20:33:37 (68.1 MB/s) - `101_ObjectCategories.tar.gz' へ保存完了 [131740031/131740031]

出来た。以前、Flask アプリを動かした時より簡単だな。

設定ファイルで起動

uWSGI 用の設定ファイル。

[uwsgi]
uid = takatoh
gid = takatoh
http = :8080
venv = /home/takatoh/w/myapp/env
wsgi-file = /home/takatoh/w/myapp/index.py
master=true
pidfile=/home/takatoh/w/myapp/myapp.pid
logger=file:/home/takatoh/w/myapp/myapp.log

起動。

takatoh@apostrophe $ uwsgi uwsgi.ini
[uWSGI] getting INI configuration from uwsgi.ini

ダウンロードのテスト。

takatoh@apostrophe $ wget http://localhost:8080/101_ObjectCategories.tar.gz
--2016-11-25 08:35:08--  http://localhost:8080/101_ObjectCategories.tar.gz
localhost (localhost) をDNSに問いあわせています... 127.0.0.1
localhost (localhost)|127.0.0.1|:8080 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 131740031 (126M) [application/x-tar]
`101_ObjectCategories.tar.gz' に保存中

100%[======================================>] 131,740,031 28.6MB/s   時間 4.5s 

2016-11-25 08:35:13 (27.8 MB/s) - `101_ObjectCategories.tar.gz' へ保存完了 [131740031/131740031]

OK。うまくいった!