PyCon APAC参加レポート第2回は、
パッケージの未来
1日目の一番初めのセッションは、

今までのパッケージングシステム
Pythonのパッケージングシステムの歴史は複雑で、
- distribute
- setuptools
またパッケージのインストールには以下のコマンドを使用します。
- pip
- サードパーティのパッケージインストーラ
- easy_
install - distribute/
setuptoolsに含まれるインストールコマンド
標準で入っているdistutilsもあるのですが、
python setup.py sdist # ソースパッケージの作成 python setup.py install # パッケージインストール
また、
- virtualenv:仮想環境の構築
- pip:パッケージインストーラ
- setuptools:パッケージ作成の補助ツール
これらはGithubとBitbucketにリポジトリが分かれているので、
easy_installとpip
パッケージのインストール方法について、
easy_install [package_name]
と書いてあったり、
pip install [package_name]
と書いてあったりします。
これらの違いは、
pip install [package_name]
# パッケージのインストール(デフォルトではPyPIからダウンロードしてきてインストール)
pip install [package_url]
# 特定URLから直接インストール
pip install [vcs{git,hg,...}+[repository_url]@[change_set]]
# GitやMercurialのリモートリポジトリから直接インストール
pip install -r requirements.txt
# requirements.txtに書かれたパッケージを全てインストール
pip uninstall [package_name]
# パッケージをアンインストール
easy_
virtualenvを使えば、
distributeとsetuptools
distributeとsetuptoolsは同じ役割を果たすライブラリですが、
マージ宣言されるまで、
setuptoolsのことは忘れてあげてください
と話していましたが、
distributeのことは忘れてあげてください
となるとおっしゃっていました。
setuptoolsはマージ後に頻繁に更新されており、
setup.py test # ユニットテスト実行
setup.py register # PyPIへの登録
setup.py sdist bdist_egg upload # ソース配布物とegg形式の配布物をPyPIにアップロード
setup.py upload_docs # ドキュメンテーションをPyPIにアップロード
その他、
from setuptools import setup, find_packages
setup(name="yourproject",
packages=find_packages(),
entry_point={
"console_scripts": [
"hello=hello:greeting",
],
})
これからのパッケージングシステム

PythonはPEPという規約に従って仕様変更が行われていきます。パッケージングシステムについてPEPで議論されている部分とそうでない部分
パッケージに関連するPEPとして以下の8つが挙げられます。
- PEP 345 -- Metadata 1.
2 - PEP 376 -- Database
- PEP 386 -- Version
- PEP 420 -- Namespace Package
- PEP 426 -- Metadata 2.
0 - PEP 427 -- Wheel
- PEP 440 -- Version
- PEP 453 -- Bundle PIP Installer
pipは先ほどの説明の通りで大きな変更はないようです。ここでは以下のWheel、
- パッケージフォーマット Wheel
- 新しいメタデータを含むzip形式のパッケージフォーマットです。電子署名も可能になっています。pipもサポートを開始しています。
- ユーティリティモジュール distlib
- pkg_
resourcesからdistlibに変更され、 今後はdistlibを使ってパッケージのメタデータにアクセスするようになります。pkg_ resourcesとほとんど同じことができるようです。 - メタデータ pydist.
json - egg-infoでかなり適当に取り決められていた仕様がまとめられ、
json形式でメタデータが扱いやすくなります。
今までまとまった情報のなかったパッケージ関連のシステムについて、
PythonとDataDogを使って簡単なシステムモニターリング
1日目の午後3番目のセッションは、
DevOps活動を積極的に行われている堀田さんから、
システムモニタリングの重要性
海外のIT系の企業では当たり前のように、

これはEtsyの社内のワンショットだそうです。海外ではスタートアップからある程度大きくなった企業まで、
システムモニタリングのキーワードは、
- Collection:データ収集
- Correlation:相関性の発見
- Collaboration:協調作業への結びつけ
これらを実行するために、
堀田さんは、
以下のように、

日本国内ではGengoが使っているそうです。
堀田さんは、

さらに、

DataDogを使う
DataDogを使えば、

このダッシュボードにはメッセージ機能も貼り付けることができ、
Windowsをはじめ、
$ DD_API_KEY=c81dc7fbacbfca8c9e4f1c02d439f112 bash -c "$(wget -qO- http://dtdg.co/agent-install-ubuntu)" $ sudo sh -c "echo 'deb http://apt.datadoghq.com/ unstable main' > /etc/apt/sources.list.d/datadog.list" $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv- keys C7A7DA52 $ sudo apt-get update $ sudo apt-get install datadog-agent $ sudo sh -c "sed 's/api_key:.*/api_key: c81dc7fbacbfca8c9e4f1c02d439f112/' /etc/dd-agent/ datadog.conf.example > /etc/dd-agent/datadog.conf" $ sudo /etc/init.d/datadog-agent start
Pythonから使うライブラリはPyPIに公開されており、
$ easy_install dogstatsd-python
データを送る際には以下の機能を使用できます。
- Counters
- Gauges
- Histograms
- Sets
- Tags
- Sample Rates
これらの機能を活用したコードがいくつか紹介されていました。
ページのアクセスごとにメトリクスをカウントアップする例def render_page():
""" Render a web page. """
statsd.increment('web.page_views')
return 'Hello World!'
def get_free_memory():
...
return free_memory
while True:
statsd.gauge('system.mem.free', get_free_memory())
time.sleep(10)
@statsd.timed('database.query.time')
def get_data():
return db.query()
コードをアプリケーションに追加し、
あとは、
発表の半分はDataDogの説明でしたが、
NagiosやMumin、
各種指標をクローリングし、
about mock
1日目の最後のセッションはBeProudで働く@podhmoさんによるmockライブラリに関するお話です。Pythonで何かを作るより遊ぶことが多いpodhmoさんは、
mockとは何か
mockはテスト時に都合の良いように、
- Stub
- 実行時に好きな値を返すようにします。入力値を置き換える機能のことを言います。
- Spy
- 実行時にどのように使われたか確認する。出力値の調査を行う機能のことを言います。
ただ、
mockライブラリの使い方
まずはStubの使い方です。ここではtarget.
from datetime import datetime
def is_holiday():
return datetime.now().weekday() == 0
def visit_shop(name):
if is_holiday():
fmt = "{name} is close."
return fmt.format(name=name)
else:
fmt = "{name} is open."
return fmt.format(name=name)
import mock
import unittest
class Tests(unittest.TestCase):
def _callFUT(self, *args, **kwargs):
from target import visit_shop #2.x
return visit_shop(*args, **kwargs)
@mock.patch("target.is_holiday")
def test_it(self, m):
m.return_value = True
result = self._callFUT("Shop")
expected = "Shop is close."
self.assertEqual(result, expected)
続いてSpyの使い方です。ここでは、
def enqueue_data(data):
# using external resource! (e.g. MQ)
message = "Enqueue Messaging Queue!"
raise Exception(data, message)
def notify_message(name):
# using external resource! (e.g. mail)
fmt = "{name} submitted!"
raise Exception(fmt.format(name=name))
def after_submit(data, name):
enqueue_data(data)
notify_message(name)
import mock
import unittest
class Tests(unittest.TestCase):
def _callFUT(self, *args, **kwargs):
from target import after_submit
return after_submit(*args, **kwargs)
@mock.patch("target.notify_message")
@mock.patch("target.enqueue_data")
def test_it(self, m0, m1):
data = mock.sentinel.Data
self._callFUT(data, "Foo")
m0.assert_called_once_with(data)
m1.assert_called_once_with("Foo")
このように、
mockを実装する
ここから、
- 模倣
(mimic, fake) - 置き換え
(patch) - 記録
(capture)
まずは模倣から実装します。以下のMockクラスを自前のMockクラスに置き換えることを目的とします。
import mock
def complex_query(qs, name):
qs = qs.where(name=name).where(pemission_id=1)
return qs.where(deleted_at=None).as_list()
m = mock.Mock()
m.where.return_value.where.return_value.where.return_value.as_list.return_value = ["Foo"]
complex_query(m, "Foo") # => ["Foo"]
これで必要なのは.
class MyMock(object):
def __init__(self, name='*'):
self.name = name
def __getattr__(self, k):
c = self.__class__(name=k)
setattr(self, k, c)
return c
def __call__(self, *args, **kw):
if hasattr(self, "return_value"):
return self.return_value
raise Exception("not callable")
この他にもisinstance()の模倣や置き換え
- greeting.
py - https://
gist. github. com/ podhmo/ 6497807
mockライブラリを使ったテストの欠点

テストに関する簡単なマトリクスがスライドに映され、
テストが成功しているのに、
ここではリファクタリングによって、
class Book(object):
def __init__(self, content):
self.content = content
def normalize(self): #dont provide pp
return self.content #do_something()
class Event(object):
def __init__(self):
self.events = []
def publish(self, v):
self.events.append(("publish", v))
def publish_pp(event): #expected pp
book = Book("user.get_book()")
event.publish(book.pp())
import unittest
import mock
class Tests(unittest.TestCase):
def _callFUT(self, *args, **kwargs):
from target import publish_pp
return publish_pp(*args, **kwargs)
@mock.patch("target.Book")
def test_it(self, m): #expected pp
m.return_value.pp.return_value = "mock"
from target import Event
ev = Event()
self._callFUT(ev)
expected = ("publish", "mock")
self.assertEquals(ev.events[0], expected)
m.return_value.pp.assert_called_with()
tests.
こういう時はspecを使うと属性のチェックも行ってくれます。patchの引数にspec=[対象のクラス]を渡すだけですね。
import unittest
from mock import patch
class Tests(unittest.TestCase):
def _callFUT(self, *args, **kwargs):
from target import publish_pp
return publish_pp(*args, **kwargs)
def test_it(self): #expected pp
from target import Book
with patch("target.Book",spec=Book) as m:
m.return_value.pp.return_value = "mk"
from target import Event
ev = Event()
self._callFUT(ev)
expected = ("publish", "mock")
self.assertEquals(ev.events[0], expected)
m.return_value.pp.assert_called_with()
この他にautospecの紹介がありましたが、
- プロパティとメソッドの区別が難しい
- インスタンス変数を関知しない
podhmoさんは、
この発表はmockライブラリについて詳しく説明されており、
次回は2日目のセッションとパーティやランチを含め、