pytest向けのpluginを書く

以前紹介したpython pluginの作り方に続いて、今回はpytestのpluginの作り方を紹介したいと思います。

そもそもpytestって何

pytest is a mature full-featured Python testing tool that helps you write better programs.

公式サイトの引用ですが、pytestはpythonのtestのための便利ツールという扱い。
ツールという言葉が正しいか難しいところですが、単にtest runnerとは言えないくらいたくさんの機能を有しています。詳細は公式サイトを参照ください。
https://docs.pytest.org/en/latest/index.html

作るもの

今回はpytestの特徴の一つである、hookの機能を利用して、setup/call/teardownでdebugメッセージを表示する、簡単なpytest pluginを作ってみようと思います。

環境

構成

GitHub - sirouma/hello_pytest

hello_pytest/
 ├ hello_pytest/
 │ ├ __init__.py
 │ └ plugin.py
 ├ test/
 │ ├ __init__.py
 │ └ test_sample.py
 └ setup.py

pluginの実態となるコードはplugin.pyで、今回は動作確認用としてtestコードも用意しています。

コード

一部のコードについて解説します。
まずは、setup.pyですが、前回紹介したpython pluginのsetup.pyからの主な変更点として、entry_pointsがあります。
pytestはentry_points pytest11からpytestの各種pluginを探しに行きます。なので、自身でpluginを定義する場合は、ここに自身のpytest pluginの情報を追加する必要があります。

続いてpluginの実態となる部分のコードになります。
今回は3つのhook関数pytest_runtest_setup, pytest_runtest_call, pytest_runtest_teardownを定義することで、それぞれのタイミングでdebugログをコンソールに出力します。
各種hookの仕様は以下を参考にしてみてください。
Writing plugins — pytest documentation
_pytest.hookspec — pytest documentation


動かしてみる

まずはインストール。setup.pyのあるディレクトリで、pip3 installを実行。

sirouma$ pip3 install ./
Processing /Users/sirouma/Workspace/hello_pytest
Installing collected packages: siro-uma.hello-pytest
Running setup.py install for siro-uma.hello-pytest ... done
Successfully installed siro-uma.hello-pytest-0.1.0

実際にpytestを使ってtestを走らせてみる。

  • sオプションをつけて、stdout/stderr/stdin capturingを無効にしましょう。

sirouma$ py.test test/test_sample.py -s
================== test session starts ==================
platform darwin -- Python 3.5.3, pytest-3.1.2, py-1.4.34, pluggy-0.4.0
rootdir: /Users/sirouma/Workspace/hello_pytest, inifile:
plugins: siro-uma.hello-pytest-0.1.0
collected 1 items

test/test_sample.py pytest_runtest_setup
pytest_runtest_call
.pytest_runtest_teardown


================== 1 passed in 0.02 seconds ==================

少し見づらいですが、pytest_runtest_setup, pytest_runtest_call, pytest_runtest_teardownがコンソールログに出力されているのがわかります。

まとめ

上記の通り、自作のpytest pluginができました。
その他にもたくさんのhookや、今回は特に触れていませんが、fixtureという便利な仕組みもあるので、ぜひ便利なpytest pluginを作ってみてください。

サンフランシスコ滞在中に便利だったアプリなど

東京に戻ってきて、サンフランシスコ中に使っていたiOSアプリを削除しているんですが、何を使っていたか備忘録として残しておきたいな、と思い、アプリ以外の情報も含めてここに残しておこうと思います。

目次

 

iOSアプリ

Uber

説明不要ですね。サンフランシスコでの移動では大変お世話になりました。

Uber

Uber

  • Uber Technologies, Inc.
  • 旅行
  • 無料

Lyft

こちらも説明不要。使用した限りではUberより心なしか安い気がする。たまたまなのかサンフランシスコ国際空港から市内に向かうときは、UberよりLyftの方がかなり安いです。1人でもシェアライドを選べば、BARTより安いことも結構多いです。

Lyft

Lyft

  • Lyft, Inc.
  • 旅行
  • 無料

Transit 

サンフランシスコ市内での公共交通機関を使う場合はこちらが便利。乗り換えの検索や、最寄りのバス停・メトロ駅の検索、バス・メトロ運行状況をリアルタイム検索できたりと、UIの使い勝手も含めて、かなり優秀アプリです。

Transit • Real-Time App for Bus, Subway & Metro

Transit • Real-Time App for Bus, Subway & Metro

  • Transit App, Inc.
  • ナビゲーション
  • 無料

Yelp

サンフランシスコでレストランを探すならこれ(賛否両論あるようですが)。レビューが高くても個人的にイマイチと感じることもあったので、Google Mapのレストラン評価と併せて参考程度にするのが良さそうです。

私は床屋の検索もYelpで行いました。

Yelp

Yelp

  • Yelp
  • 旅行
  • 無料

OpenTable

レストラン予約アプリ。サンフランシスコでも人気のお店は予約が必要です。Yelpでレストランを検索し、OpenTableで予約することもありました。おいしいご飯を並ばずに食べたい人は必須アプリ。

OpenTable

OpenTable

  • OpenTable, Inc.
  • フード/ドリンク
  • 無料

Meetup

サンフランシスコ内では毎日たくさんのMeetupが行われています。主にMeetupの検索で使っていました。

The Weather Network

 天気予報アプリです。数ある天気予報アプリの中で、かなり予報精度が高いです(少なくともサンフランシスコに関しては)。天気の変わりやすいサンフランシスコでは必需品ですね。

The Weather Network

The Weather Network

  • Pelmorex Media Inc.
  • 天気
  • 無料

Couchsurfing

私は英語力上達のため、サンフランシスコでCouchsurfingでホストをしていました。ゲストとの連絡は、Whatsappなどに移るまではこちらを利用していました。泊める・泊まる以外にも、一緒に飲みに行く人や一緒に観光する人を探すこともできます。

余談ですが、サンフランシスコ市内にあるCouchsurfingの本社では、毎月?無料で映画が上映されるイベントが開催されています。

Couchsurfing Travel App

Couchsurfing Travel App

  • CouchSurfing International Inc.
  • 旅行
  • 無料

 

Webサイト

FunCheap

サンフランシスコ近郊で行われるイベントが網羅されています。日本では見ないようなユニークなイベントも多いので、こちらで見つけたイベントに結構参加してました。

sf.funcheap.com

Eventbrite

こちらもイベントが検索できるWebサイト。こちらはiOSアプリもあるようですが、上のFunCheapの方が掲載イベントが多かったので、利用頻度は少なめでした。

www.eventbrite.com

Spirit Airlines

サンフランシスコではないですが、湾向かいのオークランド国際空港からSpirit Airlinesの飛行機が飛んでいます。LA行きなどは、直前でも結構安く買えたりします。

Spirit Airlines - cheap tickets, cheap flights, discount airfare, cheap hotels, cheap car rentals, cheap travel

最後に

すでにサンフランシスコでの生活を忘れつつあるので、何か思い出したら情報を追加しようと思います。

3ヶ月のサンフランシスコ滞在を終えて

3ヶ月の任期を終えてサンフランシスコから東京に帰ってきました。

可能ならもっと長く滞在していたかったですが、東京オフィスの業務都合もあり、延長希望は通りませんでした。笑

ということで、記憶のあるうちにサンフランシスコでの目標の達成具合を振り返って見ようと思います。

SFのエンジニアとの技術の力量の差を知る

個人的な感想としては、ソフトウェアの技術レベルに関しては日本とサンフランシスコで大きな違いはなく、優秀なエンジニアもいるし、結構平凡なエンジニアもいる、が正直な感想です。

もちろんサンフランシスコ中のエンジニアと会ったわけではないですし、私の短い滞在での知る限り、という注釈付きになりますが、職場で働いた限り&Meetupに参加した限りの感想は、上記の通りです。

ただ、ソフトウェアの技術レベル以外の、特にプレゼンテーション技術はサンフランシスコのエンジニアのが優れているなあと感じました。

話の進め方とか、聴衆を取り込む力とか、とにかく慣れている。これは、日本のエンジニアとしては見習うべき点のひとつかと思っています。

英語を上達させる

これは正直目立った上達はあまりないかな、と感じています。悔しいですが。

職場だけでなく、Couchsurferを家に招いたりとなるべく喋る機会を増やす努力をしましたが、劇的に話せるようになったとか聞けるようになった感じはしないです。

ただ、最近は発音の勉強に力を入れるようにしていて、その効果は少しずつでてきているように思います。

(社外で)友達を作る

残念ながらフランクに飲みに行けるような友達はできませんでした。が、CouchsurferやMeetupで参加したバレーボールを通して、定期的に連絡を取るような友達はできました。

この関係は大切にしていきたいと思います。

ヨセミテ国立公園に行く

こちらは無事達成。5月初旬に行ったので、雪の関係で行けないところは多々あったし、週末だけではとても周りきれななかったですけど、Gracier pointには行けたし、偉大な自然を存分に感じました。

長い時間をとって、いつかまた訪れたいと思います。

 

以上、目標に対しての振り返りです。英語はもう少しやれたかなあと思うところがあるので、ちょっと残念に思っていますが、概ね満足です。

3ヶ月という短い期間でしたが、ここに書いたこと以外にも得るものはたくさんあったし、とても良い刺激になりました。そして、より一層海外で働きたいという気持ちが増しました。

足りない部分にも色々気づけたので、この経験を今後の成長に役立てていこうと思います。

自作Pythonモジュールのpypiへの登録

少し時間が空きましたが、以前に予告した通り、自作モジュールのpypiへのアップロードに挑戦してみます。
流れは以下の通り。

  1. アカウント登録
  2. setup.pyの修正
  3. testpypiへのお試しアップロード
  4. pypiへのアップロード

では順を追って説明していきましょう。

アカウント登録

pypiへの登録にはまずアカウント登録が必要です。実際にはアップロード時にアカウントを作成することも可能なようですが、今回は事前に作成します。
アカウント登録は本番用のpypi.python.orgとテスト用のtestpypi.python.orgそれぞれに登録しておきましょう。 ユーザー名, パスワード, メールアドレスなので、特に難しいことはないと思います。(メールアドレスだけ少しルールが厳しいです。)

setup.pyの修正

前回作ったsetup.pyをpypiでのリリースに向けて修正しましょう。

from setuptools import setup


setup(
    name='siro_uma.hello',
    version='0.2.0',
    description='A sample Python project',
    long_description='This is a sample to say Hello!',
    url='https://github.com/sirouma/hello',
    author='siro_uma',
    author_email='sirouma.09@gmail.com',
    license='MIT',
    classifiers=[
    'Development Status :: 3 - Alpha',
    'Intended Audience :: Education',
    'Topic :: Education',
    'License :: OSI Approved :: MIT License',
    'Programming Language :: Python :: 3.6',
    ],
    keywords='sample setuptools development',
    packages=['hello'],
    entry_points={
        'console_scripts': [
            'hello=hello.hello:hello',
        ],
    },
)

ライセンスは1番簡単なMITをこちらのサイトを参考にしながら付与してみました。
classifiersに関しては、pypiが提供するリストの中から適当なものを選んでみています。

testpypiへのお試しアップロード

pypiへのアップロードの前に、testpypiでトレーニングしましょう。(と、pypiが申しております。Packaging and Distributing Projects — Python Packaging User Guide documentation
まずはHOME/.pypircを作成しましょう。これをやっておくと以降で出てくるregister/uploadの際にusername/passwordが聞かれなくなるので便利です。

[distutils]
index-servers =
  pypi
  pypitest

[pypi]
repository=https://pypi.python.org/pypi
username=siro_uma
password=password

[pypitest]
repository=https://testpypi.python.org/pypi
username=siro_uma
password=password

続いて前回のおさらいでインストール可能なパッケージの作成。

> python3 setup.py sdist

packagingした内容を基に、testpypiにpackageのmetadataを登録します。
まずは登録に必要なコマンドラインツールtwineをインストールし、それを使って登録を行います。

> pip3 install twine
> twine register dist/* -r pypitest
Registering package to https://testpypi.python.org/pypi
Registering siro_uma.hello-0.1.0.tar.gz

さらにpackageしたファイルを登録します。

> twine upload dist/* -r pypitest
Uploading distributions to https://testpypi.python.org/pypi
Uploading siro_uma.hello-0.1.0.tar.gz
[================================] 4350/4350 - 00:00:02

以上で、https://testpypi.python.org/pypiへの自作モジュールの登録が完了します。 念のため、インストール可能か試してみましょう。

> pip3 install -i https://testpypi.python.org/pypi siro_uma.hello
Collecting siro_uma.hello
  Downloading siro_uma.hello-0.1.0.tar.gz
Installing collected packages: siro-uma.hello
  Running setup.py install for siro-uma.hello ... done
Successfully installed siro-uma.hello-0.1.0
> hello
Hello, world!

問題なさそうなので、続いて本番環境である、https://testpypi.python.org/pypiに登録してみましょう。

pypiへのアップロード

やることはtestpypiのときと変わりません。-rでindexを指定する先が変わるだけです。

> twine register dist/* -r pypi
Registering package to https://pypi.python.org/pypi
Registering siro_uma.hello-0.1.0.tar.gz
> twine upload dist/* -r pypi
Uploading distributions to https://pypi.python.org/pypi
Uploading siro_uma.hello-0.1.0.tar.gz
[================================] 4350/4350 - 00:00:02
> pip3 install -i https://pypi.python.org/pypi siro_uma.hello
Collecting siro_uma.hello
  Downloading siro_uma.hello-0.2.0.tar.gz
Installing collected packages: siro-uma.hello
  Running setup.py install for siro-uma.hello ... done
Successfully installed siro-uma.hello-0.2.0
> hello
Hello, world!

最後に

以上でHello, world!な自作モジュールをpypiへの登録ができました。非常に簡単に自作モジュールを世に展開できるのがわかったかと思います。
参考までに、作ったモジュールへのリンクと、ソースコードのリンクを貼り付けておきます。

siro_uma.hello 0.1.0 : Python Package Index

siro-uma.hello 0.2.0 : Python Package Index

GitHub - sirouma/hello

次回はテストエンジニアらしく、pytestのプラグインの作成について記載したいと思います。

サンフランシスコで英語の学習機会を増やすためにやった3つの事

SF滞在中の目標の中で”英語を上達させる”と、高らかに宣言して早一ヶ月半。これまで私がサンフランシスコで英語の学習機会を増やすためにやったことを3つ紹介したいと思います。

Meetupへの参加

まずはMeetupの積極的参加ですね。以前ブログ内でも紹介しましたが、サンフランシスコ内では主に言語交換を目的とした英語に関するMeetupがかなりたくさんあります。
日英の言語交換や、単純に英語だけ会話しましょう的なものから、ただの飲み会まで様々です。
そこに積極的に参加することで、英語のアウトプットとインプットの量を増やすことができます。
また、英語の勉強法の情報交換なんかもできるので、英語を上達させる意味では非常にメリットが大きいと思っています。
同じ英語を勉強している人間同士ということで仲間意識も生まれやすく、友達を見つけやすいのも良い点の1つかもしれません。
私観点で、サンフランシスコ内のMeetupで比較的馴染みやすいもののリンクを貼り付けておくので、参考にしてみてください。

Nihongo Moriagari: Japanese Language Meetup (San Francisco, CA) | Meetup
Free English Conversation For International Students (San Francisco, CA) | Meetup
ILSC English Experience (San Francisco, CA) | Meetup

デーティングアプリの利用

TinderHappnなどを利用して、あわよくば外国人の彼女を見つけるのもひとつの手段です。 「その言語を学ぶのに一番の近道は、その言語を喋る異性と恋人関係になること。」とはよく言ったもので、まがいもない事実だと個人的には思っています。
恋人関係になるためのひとつのアプローチがデーティングアプリの利用になると思いますが、まあ結論から言うと、自分の場合はほとんど効果なしです 笑
マッチはしてもあまり会話が盛り上がらないので、なんの勉強にもなりません。私がデーティングアプリで効率的に勉強するには、別の何かの勉強が必要と思われます。。
色々な意味で自信のある方はぜひ試して成果を教えてほしいです(切実)
アプリもいくつかあるので自分の趣向に合わせて、選んでみてください。(以下参考)

The 8 Best Dating Apps for 2017 | Digital Trends

カウチサーフィンの活用

カウチサーフィンがどれほどの市民権を得ているか謎ですが、簡単に説明すると、カウチサーファーとは、人の家にあるカウチ(ソファ)を寝床にしちゃう人たちのことで、カウチサーファーと寝床となるカウチを提供するホストをマッチングさせてくれるウェブサービスが以下のサービス。 www.couchsurfing.com

で、私が実践しているのは、サーファーをホストする側。幸い今住んでいるアパートメントがまあまあな広さで、素敵なカウチもついているので、サンフランシスコに来てからホストをしてみています。
知らない人を家に入れるのは多少は抵抗はありますが、やってみるとなかなか楽しくて、すでに5人のカウチサーファーをホストしてきました。
英語の学習機会に対する貢献は絶大で、一緒にいる時間が長いこともあり、否応にも話さなくてはいけないので、英語の使用頻度は劇的に増えます。
また、生活する上で必要な生の英語を使う機会が増えるのも、良い点と言えると思います。
今のところ変な事も起きていませんし、ややストレスを感じることもありますが、意外となんとかなるものですし、英語を使えること以上に色々な人に会えることが楽しいです。
ということで、私はカウチサーフィンのホストを1番オススメします。(結構人を選びそうですが、、)

最後に

以上が私が英語の学習機会を増やすためにやっている3つのことになります。
海外に来てみたけど、意外と英語を使う機会がない。というような方はぜひ上記のアプローチを試してみてください。
もちろん、これ以外にも発音や語彙を増やすための自宅学習なども併せてやらないと、思うような効果はでてこないと思いますので、上記の取り組みと併せて自宅学習も行うようにしましょう。

はじめてのKerasと深層学習

先日、MeetupでKerasと深層学習の勉強をしてきました。 参加したのはこのMeetup: www.meetup.com

内容

Meetupとして以下の3つのTutorialを用意していて、そのうちの1つのDeep learningに関するTutorialを受けてきました。

  1. Working with Binary Data
  2. Sqlite3 and PEP249: A Beginner’s Course
  3. Intro to Deep Learning with Keras

講義用のスライドとGithubのrepositoryもシェアされているので、貼り付けておきます。

GitHub - Dataweekends/intro_deep_learning_sf_python_meetup: Repository for the Python meetup tutorial in SF April 2017

www.slideshare.net

内容は、Kerasを使ってMNISTの手書き数字を認識する深層学習のモデルを作るというもの。 後で知りましたが、これはKerasに含まれるサンプルコードのひとつのようです。。 サンプルコードをExercise形式で、順に説明を含めながら読み解いていく形の講義でした。

コードと説明(インライン)

説明というほどのものでもないのですが、自分の理解のためのメモ書きを含めたコードが以下になります。

# 必要moduleのimport
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.optimizers import RMSprop
from keras.utils import to_categorical
# the data, shuffled and split between train and test sets
(X_train, y_train), (X_test, y_test) = mnist.load_data('/tmp/mnist.npz')
# 60000枚の白黒画像 (28pixel x 28pixel)
print("X_train shape:", X_train.shape)
# 10000枚の白黒画像 (28pixel x 28pixel)
print("X_test shape:", X_test.shape)
# 1画素 unsigned int 8 bit (256階調)
print("Data type:", X_train.dtype)
# Exercise 1:
# Reshape your data so that each image becomes a long vector
# Your code here
# uint8の2次元配列を1次元に展開(784次元のベクトル)
X_train = X_train.reshape(60000, 784)
X_test = X_test.reshape(10000, 784)
# Exercise 2:
# change the type of the input vectors to be 'float32'
# and rescale them so that the values are between 0 and 1
# Your code here:
# uint8をfloat32に変換
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
# 0~255の階調データを1, 0に2値化(binary)
X_train /= 255
X_test /= 255
# Exercise 3:
# convert class vectors to binary class matrices
# using the keras.utils.to_categorical function
# Your code here:
# to_categorical(y, nb_classes=None):  0~(nb_classes-1)までを持つクラスベクトルyをバイナリのクラス行列に変換
# 0~9をもつクラスベクトルy_trainをバイナリのクラス行列に変換
# ex: 5 -> 1_0000, 8 -> 1000_0000
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
# Exercise 4:
# Define a fully connected model using the Sequential API
# https://keras.io/getting-started/sequential-model-guide/
# Choose your architecture as you please
# Your code here:
# Sequentialは層の線形スタック
model = Sequential()
# addで層を追加できる。Denseは全結合ニューラルネットワークレイヤー
# ReLUを活性化関数にinput 784次元ベクトルにを512次元ベクトルに
model.add(Dense(512, activation='relu', input_shape=(784,)))
# Dropout 20%の入力を0として扱い過学習を防止(汎化促進)
model.add(Dropout(0.2))
# ReLUを活性化関数として層を追加
model.add(Dense(512, activation='relu'))
# Dropout 20%の入力を0として扱い過学習を防止(汎化促進)
model.add(Dropout(0.2))
# softmaxを出力層の活性化関数にすることで
# ニューラルネットワークの出力があたかもあるクラスに属する確率を表しているとして学習させることができる
model.add(Dense(10, activation='softmax'))

model.summary()
# Exercise 5:
# Compile your model using an optimizer of your choice
# make sure to display the accuracy metric
# https://keras.io/optimizers/
# Your code here:
# 誤差関数(損失関数)としてクロスエントロピー関数を設定
# 最適化(モデルの予測値と実際の値との誤差から、パラメータ(重み)を更新すること)にRMSpropを使う。
model.compile(loss='categorical_crossentropy', optimizer=RMSprop(), metrics=['accuracy'])
# Exercise 6:
# Fit the model on the training data. Use 30% of the
# data as validation. Experiment with different batch sizes
# and number of epochs. Save the history of training and print it.
# Your code here:
# X_train: 入力となるnumpy配列、y_train: ラベルとなるnumpy配列
# batch_size: 設定したサンプル数ごとに勾配の更新
# epochs: モデルを学習するエポック数
# verbose: 0 とすると標準出力にログを出力しません. 1 の場合はログをプログレスバーで標準出力
# validation_split: float (0. < x < 1) 型で, 検証のデータとして使うデータの割合
history = model.fit(X_train, y_train, batch_size=1024, epochs=10, verbose=1, validation_split=0.3)

print(history.history)
# Exercise 7:
# Calculate the score on the test data using `model.evaluate`
# Your code here:
# バリデーションデータによる評価とloss/accuracyの確認
score = model.evaluate(X_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

所感

Kerasを使ってみての感想は、複雑なモデルを簡単に作れちゃうんだな。という印象。(深層学習的に複雑なモデル、という意味ではなく)
ただ深層学習の素人だったので、1行1行何をやっているのかを理解するが非常に大変でした。専門用語も知らないものが多い上に英語だったので、残念ながら講義にはついていけなかったのが実際です。
家に戻った後の復習として、以下のサイトとKerasのDocumentを参考に、なんとなく理解が追いついた感じです。
何はともあれ、新しいものに触れるいい機会だったので、引き続き深層学習の勉強に取り組みたいと思います。

高卒でもわかる機械学習 (0) 前置き | 頭の中に思い浮かべた時には

おまけ

同じMeetup内で講義された資料もせっかくなので貼り付けておきます。 自分も後で読んでみようと思います。

Bradfield School of Computer Science

[Python] SF Python Project Night 4/19/2017: Working with Binary Data - Pastebin.com

pythonの配布可能なpackageの作り方(setuptoolsを使ってhelloコマンドを作ろう)

珍しく技術ネタです。
今日はpythonでsetuptoolsを用いて、配布可能なpackageを作りたいと思います。

やりたいこと

  • ”Hello, world!”と標準出力に表示するhelloというコマンドを作る。
  • 上記を、setuptoolsを用いてPythonの配布可能なpackageで実現する。

環境

構成

root/
 ├ hello/
 │ ├ __init__.py
 │ └ hello.py
 └ setup.py

コード



説明

説明するほどでもないですが、setup.pyの内容について少し付け加えます。
packagesについては、hello.pyのあるpackageであるhelloのみをpackageとして登録しています。
複雑になっていく場合はsetuptoolsのfind_packages()を使ったほうが楽に済ませられます。(参照:Building and Distributing Packages with Setuptools — setuptools 35.0.0 documentation
entry_pointsについては、command名helloをhello package内にあるhello.pyのhello functionと関連付けることで?、commandとしてhelloが実行可能になります。

動かしてみる

packageのbuild&install

> python3 setup.py install
> hello
Hello, world!

packageのbuildのみ

> python3 setup.py sdist
> ls dist
hello-0.0.1.tar.gz

最後に

以上で、helloコマンドが実現可能になりました。
次回はもうpypiにpackageを登録するところをやってみたいと思います。