danglingfarpointer's memoization

仕事周りでの気付き、メモ、愚痴などを書いていきます。

Pythonのunittestとcoverage

PythonのUnit Testとカバレッジ計測のメモです。

Pythonの標準的なUnit Testフレームワークとして、unittestがあります。 テストの書き方はいわゆるxUnitとよく似ています。

テスト対象のサンプルクラスuser.pyです。2つのメソッドを定義しています。

class User:
    def __init__(self, name, email):
        self.__name = name
        self.__email = email

    def __str__(self):
        return '[' + self.__name + ', ' + self.__email + ']'

    def get_name(self):
        return self.__name

    def get_email(self):
        return self.__email

そしてunittestを使ったコードtest_user.pyです。Userの2つのメソッドについてテストを書いています。

import unittest
from user import User

class TestUser(unittest.TestCase):
    def setUp(self):
        self._user = User('taro', 'taro@example.com')
        
    def test_get_name(self):
        self.assertEqual('taro', self._user.get_name())

    def test_get_email(self):
        self.assertEqual('taro@example.com', self._user.get_email())

if __name__ == '__main__':
    unittest.main()

以下のようにテストを実行できます(python test_user.pyと直接実行してもOK)

 $ python -m unittest test_user

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

次はカバレッジです。カバレッジを計測するツールとして、coverageがあります。

http://blanktar.jp/blog/2015/03/python-unittest-coverage.html

pipでインストール後、coverage runコマンドでunittestのテストケースを走らせます。

 $ coverage run test_user.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

カバレッジ測定結果が.coverageに生成されます。それをhtmlに変換して、ブラウザで見てみます。

 $ coverage html user.py
 $ open htmlcov/index.html 

ファイルごとの実行割合がまず一覧で表示されます。ここでは1ファイルしかありませんが。 f:id:danglingfarpointer:20160128212908p:plain

ファイル名をクリックすると、そのファイルで実行されなかった文が一目でわかります。いい感じです。 f:id:danglingfarpointer:20160128213025p:plain

PythonのrequestsでmultipartでPOST

PythonのHTTP clientとしてrequestsという便利なライブラリがあります。それをつかってMultipartデータをPOSTするメモです。

参考: http://stackoverflow.com/questions/12385179/how-to-send-a-multipart-form-data-with-requests-in-python

POSTするデータは、(ファイル名, データ, Content-Type)あるいは(ファイル名, データ, Content-Type, ヘッダー)というタプルで与えます。

クライアントからname, email, imageを送信する例です。

>>> import requests
>>> r = requests.post("http://localhost:8080/add", files = {
     "name": ("", "hoge", "text/plain; charset=UTF-8"),
     "email": ("", "hoge@example.com", "plain/text; charset=UTF-8"),
     "image": ("hoge.png", open('hoge.png', 'rb'), "image/png")
     })

サーバー側での受信結果。ファイル名を空にすると、multipartの各要素のfilenameプロパティが与えられません。

--0a3d87b93d8c468b9b4dc7a493fbeee3
Content-Disposition: form-data; name="image"; filename="hoge.png"
Content-Type: image/png

..................<image data>.............................
--0a3d87b93d8c468b9b4dc7a493fbeee3
Content-Disposition: form-data; name="name"
Content-Type: text/plain; charset=UTF-8

hoge
--0a3d87b93d8c468b9b4dc7a493fbeee3
Content-Disposition: form-data; name="email"
Content-Type: plain/text; charset=UTF-8

hoge@example.com
--0a3d87b93d8c468b9b4dc7a493fbeee3--

OpenCV 3でPythonからSIFT

OpenCV 3では、ライセンスやパテント上の理由だろうか、SIFTやSURFはopencv_contribという別モジュールに分かれてしまっている。

opencv_contribを使うには、インストールの際に--with-contribオプションを付ける必要があるようだ。Mac OSだとHomebrewで以下のようにインストールできる。

brew install python
brew install opencv3 --with-contrib
brew link opencv3 --force

これでSIFTが使えるようになる。

# -*- coding: utf-8 -*-

import cv2

def main():
    img = cv2.imread('image.jpg', 0)

    # detector = cv2.FeatureDetector_create('ORB')
    detector = cv2.xfeatures2d.SIFT_create()
    keypoints = detector.detect(img)

    out = cv2.drawKeypoints(img, keypoints, None)

    cv2.imshow('sift', out)
    cv2.waitKey(0)

if __name__ == '__main__':
    main()

玉音放送をMacからGET

8/1付で玉音放送の音源が公開された。しかしWindowsのストリーミング形式で配信されているため、Macで鑑賞するには少し難がある。そこでMacでもうまく聴く方法をちょっと調べてみた。

ざっくり結論。Macffmpegを使ってストリーミングデータをGETし、ローカルでOGGに再エンコードすれば、ファイルとしてうまいこと再生できるようになった。

$ sudo port install ffmpeg
$ ffmpeg -i mmsh://wmt9-od.stream.ne.jp/vod11/kunaicho/hp/s200815.wma -f ogg s200815.ogg

OGGMacのたいていのブラウザで再生できる。

エンコードしたので音質が落ちたことは否めないだろうが、お手軽に玉音放送を愛でられるようになったのだ。

Dockerの使い方

Dockerの使い方について調べたことをメモします。

  • 動作環境: Amazon Linux
  • 2016/5/15更新。dockerのバージョンは1.9.1で確認

インストールと起動

sudo yum install docker
sudo chkconfig docker on
sudo service docker start

以降の操作は全てrootで行うと仮定。

イメージの取得

pullコマンドでイメージを取得する。

# docker pull centos:6.6

ここではcentosの6.6のイメージを指定している。イメージは、レポジトリとタグにより指定される。centosリポジトリで、6.6がタグである。

デフォルトでは、Docker Hubという「レジストリ」からイメージを取得することになっている。

レジストリにどのようなリポジトリがあるかは、searchコマンドで検索できる。ただしタグは検索できない。

# docker search <search-keyword>

イメージ一覧の表示

imagesコマンドでイメージがダウンロードされていることを確認する。

# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              6.6                 8b44529354f3        2 weeks ago         202.6 MB

コンテナの起動

runコマンドでイメージと何らかのコマンドを指定することで、コンテナが起動する。ここではコマンドとしてbashを起動する。

# docker run -ti centos:6.6 /bin/bash

-tは仮想端末を割り当てるオプション、-iはSTDINを開いたままにするオプションで、シェルのようにインタラクティブに操作する場合はいずれも必須。

コンテナの中で操作

コンテナでhttpdをインストールしてみる。

# docker run -ti centos:6.6 /bin/bash
[root@11dd19074528 /]# yum install -y httpd
[root@11dd19074528 /]# echo 'hello world' > /var/www/html/index.html
[root@11dd19074528 /]# exit

exitすると/bin/bashが死んでコンテナが停止する。しかしコンテナが停止しても、httpdのインストールがなされた状態のままで残る。どのようなコンテナが残っているのかは、docker ps -aコマンドで確認できる。

# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS                     PORTS               NAMES
11dd19074528        centos:6.6          "/bin/bash"         About a minute ago   Exited (0) 5 seconds ago                       condescending_shockley

イメージ化

コンテナをイメージとして保存(commit)することで、後に使いまわせるようになる。ここではcentos:httpd_installedというイメージ名でイメージ化する。

# docker commit -m 'install httpd' <image-id> centos:httpd_installed
# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
centos              httpd_installed     53c383ebc5dc        15 seconds ago      270.9 MB
centos              6.6                 8b44529354f3        2 weeks ago         202.6 MB

なお、イメージ化してもコンテナは残り続けるので、rmコマンドで明示的にコンテナを削除する。

# docker rm `docker ps -aq`

イメージからhttpdを起動

上で作成したイメージcentos:httpd_installedから、httpdを走らせてみる。

2つの方法がある。1つは、docker runhttpdを指定する方法。

# docker run -d -p 8080:80 centos:httpd_installed /usr/sbin/httpd -DFOREGROUND

ここでのポイントは以下のとおり。

  • httpdをフォアグラウンドで実行されるように指定
  • -dオプションにより、コンテナをバックグラウンドで起動
  • -pオプションにより、ホストの8080をコンテナの80にフォワード

もう1つは、一度コンテナで/bin/bashを走らせ、httpdをバックグラウンドで起動後、Ctrl-P, Ctrl-Qでコンテナからデタッチする方法。

# docker run -p 8080:80 -ti centos:httpd_installed /bin/bash
[root@9964dde4e945 /]# service httpd start
[root@9964dde4e945 /]#   # detach; Ctrl-P Ctrl-Q
# 

1コンテナ1プロセスが定石とされていることから、1つめのほうが望ましそうだ。参照: http://docs.docker.jp/engine/articles/dockerfile_best-practice.html

どちらの方法でも、ホストからwgetを打てばhttpdが動作していることを確認できる。

# wget http://localhost:8080/index.html

httpdを監視

コンテナの中のhttpdを監視するケースを考える。

1つめの方法の場合(docker runhttpdを直接立ち上げた場合)、docker execを使うことで、コンテナで一時的に/bin/bashを走らすことができる。

# docker exec -it `docker ps -q` /bin/bash
[root@9beb6db80222 /]# /etc/init.d/httpd status # 状態確認
httpd (pid  1) is running... 
[root@9beb6db80222 /]# exit
exit

また、2つめの方法の場合(docker runで一度コンテナに入ったあとにバックグラウンドでhttpd起動)、docker attach <container-id>を使うことで、デタッチしたコンテナに再びアタッチできる。

コンテナの停止

stop/killコマンド。

# docker stop <container-id>

参考) stopとkillの違い: http://elendia.hatenablog.com/entry/2014/09/16/040659

再び起動するときはdocker startコマンド。

# docker start 9beb 

イメージの削除

イメージを削除したい場合は、docker rmiコマンド。

# docker rmi <image id>

スクリプト

Dockerfileにより、イメージ作成をスクリプト化できる。

以下の内容でDockerfileという名前のファイルを置く。

FROM centos:6.6

RUN yum -y install httpd
RUN echo 'Hello world' > /var/www/html/index.html
EXPOSE 80
ENTRYPOINT /usr/sbin/httpd -DFOREGROUND

Dockerfile作成後、buildコマンドでイメージを作成。

# docker build -t centos:httpd_installed .

-tでイメージ名を指定する。末尾の.は、「Dockerfileを含むディレクトリ」を指定している。

そしてrunでコンテナを起動すると、DockerfileのENTRYPOINTで指定したコマンドが実行される。

# docker run -p 8080:80 -d centos:httpd_installed

AWSでCentOS 5をインポート

CentOS 5の大雑把なAWSインポート手順。以下のページを参考に、比較的簡単にできた。

参考) http://understeer.hatenablog.com/entry/2014/04/09/172554

実行環境はMac OS X. 手順としては、

ec2-api-toolsとawscli, あとVirtualBoxをインストールしておく。

# brew install ec2-api-tools
# brew install awscli

1. VirtualBoxCentOS 5.Xのイメージ作成

CentOS 5のisoをどこかからダウンロードしてインストール。VMの形式はvdiを選択しておく(*)

(*) VMWareの形式であるvmdkを選択することもできる。実際、後のec2-import-instanceでvmdkがサポートされているので、vmdkで作ったほうがベターである。しかし、ec2-import-instanceの際に、なぜか暗号の方式が違うというエラーで失敗。なので、一旦vdiで作って次の手順でrawに変換している。なお、Virtual PCの形式であるvhdを選択することもできるが、試してない。

なお、rootパスワードでsshログイン出来るようにしておく。デフォルト設定では出来る。

2. vdiからrawに変換

# VBoxManage clonehd /path/to/CentOS5.11.vdi /path/to/CentOS5.11.raw --format RAW

3. VMイメージをインポート

あらかじめS3のバケットを作っておき、そこにVMイメージをアップロードする。

# ec2-import-instance /path/to/CentOS5.11.raw
     -t t2.micro \
     -f RAW \
     -a x86_64 \
     -p Linux \
     --region ap-northeast-1 \
     --availability-zone ap-northeast-1a \
     --bucket bucket_name \
     --subnet subnet-aaaaaaaa \
     -o S3_OWNER_ACCESS_KEY \
     -w S3_OWNER_SECRET_KEY \
     -O AWS_ACCESS_KEY \
     -W AWS_SECRET_KEY

S3にアップロード後、ec2インスタンスが作成される。ec2インスタンスを立ち上げ、sshでrootログインできることを確認したら(*), 一旦stop.

(*) 運用時はもちろんもっとセキュアにすべき。。。

4. AMIを作成

マネージメント・コンソールなどからAMIを作成。これで基本的には完了。

5. S3に上げたVMイメージを削除

最後にS3に残ったVMイメージを削除。おしまい。

# aws s3 rm --recursive s3://bucket_name/raw_image_uuid

高遠 桜祭りの夜桜

長野 高遠の桜は、2015年は4/11, 12あたりがピークだったようだ。まさにその期間に夜桜を見に行った。

f:id:danglingfarpointer:20150501222600j:plainf:id:danglingfarpointer:20150501222526j:plain

東京の上野公園などとは大違いで、酒臭くない。宴会は禁止のようだ。まさに天下の桜を愛でることができたのだ(^^