主にプログラミングに関して。Python, .NET Framework(C#), JavaScript, その他いくらか。
記事にあるサンプルやコードは要検証。使用に際しては責任を負いかねます

AppVeyorを使ってWindowsでPythonプロジェクトのCI

                
tags: python CI
 GithubでCIといったらTravisCIと連携がポピュラーだと思う。TravisCIで環境として用意されてるのはUbuntuとOSX。
 WindowsでもPythonが使えることが近年はプッシュされている。VisualStudioではPython導入のツールが用意され、Django、Flaskなど有名フレームワークでWebアプリを作るテンプレートがある。それを作ったらAzureでホスティングとか。
 そういうわけでPythonを書いたらUbuntuやOSXだけでなく、WindowsのCI環境も欲しいよねというときにAppVeyor

 下の画像はデフォルト状態でlsコマンドでC:/直下のディレクトリを表示したものである。今回使うPythonはもちろん、RubyやGoも用意されているし、データベースとしてMongoDBも入っている。Windowsだからといって決してMS製品のみへ特化しているわけではなさそう。
1510061945253.jpg


 Github連携はどう使えばいいかというとTravisCIと同様である。GithubアカウントとのOAuth接続を許可し、マイページの自分のリポジトリ一覧から連携したいものをオンにする。あとはプロジェクトのルートにappveyor.ymlを置いてコミット、それをGithubサーバーへ送れば自動でappveyor.ymlの内容に沿ってビルドが始まる。

 GithubリポジトリからPythonで最小限のことをやっていそうなappveyor.ymlを探した。
https://github.com/jkbrzt/httpie/blob/master/appveyor.yml
 参考になるものを得て書いた自分のPythonプロジェクト用のappveyor.ymlが下記である。テスト用にPythonライブラリであるpillowが必要なプロジェクトになっている。
init:
- ECHO %PYTHON%
environment:
matrix:
- PYTHON: C:/Python27-x64
- PYTHON: C:/Python34-x64
- PYTHON: C:/Python33-x64

install:
- ECHO "Filesystem root:"
- ps: "ls \"C:/\""

- ECHO "Installed SDKs:"
- ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\""

- ps: (new-object net.webclient).DownloadFile('https://raw.github.com/pypa/pip/master/contrib/get-pip.py', 'C:/get-pip.py')
- "%PYTHON%/python.exe C:/get-pip.py"
- "%PYTHON%/Scripts/pip.exe install pillow==2.9.0"

test_script:
- '%PYTHON%\python.exe setup.py test'


 まずinitで初期化設定を行っている。といっても特にやることはなくて、テストするPythonのバージョンの表示を行っている。これは次に行うenvironmentの次に書けば良かったかも。
 initの下にあるenvironmentではビルドテストを行う環境を指定している。Pythonの複数のバージョンで使えるものを書いたので、複数のPython環境を指定している。ビルドテストはそれぞれのPython環境で行われることになる。
 installでは文字通り必要なもののインストールを行っている。今回必要なのは実は下から三行のみである。そこでpipをインストール、さらにそれを使ってpillowをインストールしている。
 test_scriptではテストコマンドを書いている。

 テストが通るとバーが緑色になる。
1510062016444.jpg

 Github連携でおなじみのバッジもある。デフォルトではTravisCIと似たようなバッジが得られるが、それと差別化してshields.ioを通して下記のようなバッジに変換しているケースが多かった。
1510062017480.jpg
https://img.shields.io/appveyor/ci/[your AppVeyor ID]/[repositry name].svg?label=windows%20build
スポンサーサイト



Python: セキュアなトークン取得方法

                
tags: python
 CSRF対策などに使うトークンはUUIDを使うのがいいのかなとこれまで考えていたが、気になるコメントをある記事で見つけた。
http://www.tokumaru.org/d/20110127.html
 UUID4なら十分なランダム性が得られるので問題ないかと思っていたが、仕様として”Do not assume that UUIDs are hard to guess”とも記述されていた。UUIDを推測するのが難しいものだと思うなと。

 StackOverflowにセキュアなトークンをどう出すかと、どうしてまずいかを解説しているものを見つけられらた。要はUUIDではセキュアといえるほどの乱雑さはないらしい。つまり無作為に作られるユニークIDとして使うには問題ないが、認証用のトークンとして使うには予想がどうにかついてしまうということだろう。
http://stackoverflow.com/questions/817882/unique-session-id-in-python/6092448#6092448

Python: raw_inputをデバッグ時のみ書き換えてみる

                
tags: python
 キーボードから入力を取るPythonスクリプトを書くことがあったが、デバッグするのにいちいちキーボード入力していくのがめんどうだった。なのでスクリプトの頭のほうに下記のスクリプトをつけて手入力を省けるようにした。INPUTという変数に入力区切りごとに改行を入れながら入力を順番に書いていく。これで以降ではraw_inputをコールするたびに入力しておいた文字列が返されていく。

# teeeeeeeeeeeeeeeeeeeeeeeeeeestooooooooo
INPUT = """1 2 3
10 20 30"""

def get_raw_input(input):
li = input.split("\n")
def inner():
return li.pop(0)
return inner

raw_input = get_raw_input(INPUT)
# remoooooooooooooooooooooooooove

Python: ステガノグラフィで画像に画像を埋め込む

                
tags: python
 ググってみたら二番煎じだったネタ……




 二つの画像を見比べる。

150401_foo.png

150401_embedded.png

 ステガノグラフィを利用して、上の画像の画素情報部分にあるJPEGデータを埋め込んだのが下の画像である。ステガノグラフィを使ってるのでファイルサイズが増大なんてこともない。どんなJPEGデータが埋め込まれているかというとこの下のポスター画像で、埋め込まれた画像と上の画像の解像度の差はほとんどない。そんなのが埋め込めるなんてJPEGの圧縮率はやっぱり高い。
150401_thumbnail.jpg

 下にステガノグラフィをPythonで実装したスクリプトを付けておく。実はだいぶ前に書いたものにちょっと手を加えたもの。
http://elicon.blog57.fc2.com/blog-entry-189.html
 埋め込みは"python stegano.py origin.png thumb.jpg"、埋め込んだ画像の取り出しは"python stegano.py embedded.png"でできる。
import sys
import struct

from PIL import Image


def embed(file, thumbnail):
with open(thumbnail, "rb") as f:
data = f.read()
length = struct.pack(">L", len(data))
string = length + data
src = Image.open(file)
newImg = src.convert('RGBA')
width, height = src.size
if width * height < len(string):
raise ValueError("too small size image")
im = newImg.load()
x, y = 0, 0
for value in string:
pixel_data = [0, 0, 0, 0]
binary = bin(ord(value))[2:].zfill(8)
for num in range(4):
bin_val = bin(im[x, y][num])[2:].zfill(8)
manipulated = bin_val[0:6] + binary[2 * num: 2 * num + 2]
new = int(manipulated, 2)
pixel_data[num] = new
im[x, y] = tuple(pixel_data)
x += 1
if x >= width:
x = 0
y += 1
newImg.save("embedded.png")
print("output: embedded.png")


def pick(file):
src = Image.open(file)
if src.mode != "RGBA":
raise ValueError()
width, height = src.size
x, y = 0, 0
im = src.load()

string = b""
for x in range(4):
pixel_data = b""
for num in range(4):
bin_val = bin(im[x, y][num])[2:].zfill(8)
pixel_data += bin_val[6:8]
string += chr(int(pixel_data, 2))
length = struct.unpack(">L", string)[0]

string = b""
x = 4
while length != len(string):
pixel_data = b""
for num in range(4):
bin_val = bin(im[x, y][num])[2:].zfill(8)
pixel_data += bin_val[6:8]
string += chr(int(pixel_data, 2))
x += 1
if x >= width:
x = 0
y += 1
if y >= height:
break
with open("thumbnail.jpg", "wb") as f:
f.write(string)


if __name__=='__main__':
if len(sys.argv) == 3:
embed(sys.argv[1], sys.argv[2])
elif len(sys.argv) == 2:
pick(sys.argv[1])


 どういう実装をしてるか以前に書いてなかったので今回はそのあたりを。
 RGBAチャンネルを持ったPNG画像をロードして、1ピクセルごとに、埋め込みたいデータを1バイトずつうまく埋め込んでいる。
 1バイトのデータを2ビット×4つに分割し、1ピクセルあたりのRGBA各色の値の下位2ビットを上書きする。各色とも下位2ビットが変更されるが、下位2ビットが色の見た目に与える影響は限定的なため、埋め込み前と埋め込み後で画像で見てみると違いを判別することは難しい。

 ステガノグラフィは暗号化の技術として使われるようだが、今回は思い付きをやってみたくてやった。これをそのままセキュリティ関連で使うのは無理があるだろう。

Python: Tornadoのノンブロッキングという特徴を手っ取り早く試す

                
 PythonでのWebアプリ作りはGoogleAppEngineから入った。GoogleAppEngineで使うフレームワークが最初はwebappでそれがwebapp2になったが、それが一般的なフレームワークではないので、PythonのWebフレームワークで他のサーバでも使えて開発が続いているのを選ばねばとなった。それでwebapp系に割と似た書き方をするし面白い特徴を持っているTornadoを選んだ。
 Tornadoはノンブロッキングなサーバというのをウリとしている。ノンブロッキングという特徴を利用すれば、時間のかかるIO処理があったらそのデータ処理開始はIOが終わるまで後回しにして、次のリクエストの処理を先にやってしまえる。このあたりを試していなかったので今回試してみる。ちなみにノンブロッキングではなくPython標準のWSGIでも使える。

 リクエスト処理中に起こった時間のかかるIO処理は後回しにして、次のリクエストの処理を始める。これを検証する。
 時間のかかるIO処理として、MongoDBから10MBのデータを取ってくる処理を行うことにする。そのためにMongoDBを立ち上げて、サイズが10MBほどの適当なドキュメントを一件作成しておく。PyMongoを使うとおおよそ下記のように。
v = "a" * 10 ** 7
db.tekitou.insert({"x":v})


 以降のPythonスクリプトではPyMongoではなくTornado用に書かれたMotorを使う。

 上記のドキュメントの取得を、Tornadoの任意のリクエストハンドラの処理にはさむ。
class AsyncHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
data = yield db.tekitou.find_one()
self.write(data["x"])


 軽い処理ですんなり終えられそうな処理をするリクエストハンドラも用意しておく。
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")


 二つのリクエストハンドラを書いた。これらをパスへ割り当てる。今回は軽い処理しかないリクエストハンドラをルート"/"、MongoDBから10MB取ってくる処理を持つリクエストハンドラをパス"/b"へ割り当てておく。
 もしパス"/b"へのリクエストが先にあり、次にパス"/"へのリクエストがあったのに、パス"/"にリクエストしたクライアントが先にレスポンスを得られれば順序が変わっており、ノンブロッキングな特徴が確認できたことになる。

 Pythonのmultiprocessingモジュールを使って、用意したサーバに計四つのプロセスでリクエストを送り、リクエスト順に対するレスポンス順の変化があるかを調べる。リクエストに使ったスクリプトを下につけておく。まずパス"/b"へのリクエストを先に二件発生させ、次にパス"/"へのリクエストを二件発生させている。リクエストごとに固有の整数値を与えておいて、それを判別のためにリクエストクエリに入れておく。
from urllib import request
from multiprocessing import Process
import datetime
import time


url1 = "http://localhost:8888/"
url2 = "http://localhost:8888/b"


def req(url, request_id):
time.sleep(0.01 * request_id)
t = datetime.datetime.now()
print(str(request_id) + ":" + str(t))
f = request.urlopen(url + "?num=" + str(request_id))
print(str(request_id))


if __name__ == '__main__':
p1 = Process(target=req, args=(url2, 1))
p1.start()

p2 = Process(target=req, args=(url2, 2))
p2.start()

p3 = Process(target=req, args=(url1, 3))
p3.start()

p4 = Process(target=req, args=(url1, 4))
p4.start()


 サーバ側は最終的に下記。どのプロセスからのリクエストかをGETのクエリから拾っている。
import tornado.ioloop
import tornado.web
import motor


client = motor.MotorClient()
db = client.tornado_test


class MainHandler(tornado.web.RequestHandler):
def get(self):
print(self.get_argument("num"))
self.write("Hello, world")


class AsyncHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
print(self.get_argument("num"))
data = yield db.tekitou.find_one()
self.write(data["x"])


application = tornado.web.Application([
(r"/", MainHandler),
(r"/b", AsyncHandler),
])


if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()


 スクリプトの用意ができたので走らせる。MongoDBを立ち上げた状態でTornadoサーバを立ち上げ、リクエストスクリプトを実行する。コマンドプロンプトをキャプチャしたものを下に。上がサーバを走らせているプロンプト、下がリクエストを書いたスクリプトを走らせたプロンプトである。
1503291752257.jpg
 サーバを走らせている上側を見るとリクエストIDの1から順番にリクエストを受け取っているのがわかる。次に下を見るとリクエストも確かにその順番で発生していると思われる。しかしレスポンスの順番がかわっており、3、4、1、2の順になっている。
 3と4は重いIOの発生しないリクエストハンドラへのリクエストであった。3と4のリクエストが1と2のリクエストのあとに発生しているのに、レスポンスを受け取ったのは3と4のほうが早い。これはMongoDBへの10MBのIO処理を待っている間に重いIO処理を含まない3と4への処理を先に済ませたからだと考えられる。つまりTornadoのサーバであるノンブロッキングという特徴が見られたということだろう。

 数件のリクエストに対するレスポンスの順番変更があるかを探ることで、Tornadoサーバのノンブロッキングという特徴を試してみた。確かに重いIO処理を行っていると考えられる最中にも別のリクエストを処理し、レスポンスの順番が変わっていた。Tornadoの特徴であるノンブロッキングというところが確認できた。
プロフィール

h

Author:h

最新記事
リンク
作ったものなど
月別アーカイブ
カテゴリ
タグリスト

検索フォーム
Amazon