読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

Pythonによる CLI ツールの実装と配布

こんにちわ。日々の作業の80%がターミナルで完結する原です。

最近、他チームに向けてCLI(コマンドライン・インタフェース)ツールを作成する機会がありました。 ツール動作環境がOS Xである点、モジュールのインストールが簡単である点、内部DSLの実装が比較的用意である点などを考慮し、Pythonで実装・配布しました。

また、自分自身も、普段の開発で行うような定形作業をPythonでスクリプト化することで、効率化しています。

シェルスクリプトでは実装が面倒くさいような外部サービスとの連携も、Pythonであればサードパーティのモジュールや例がたくさんあるため、実装が容易です。

当エントリでは、このように色々なシーンで活用できるPython製CLIツールの実装および配布方法をご紹介します。

サンプルとして「wonder_tool」という名前のCLIツールを作ってみましょう。

サンプルの仕様

このツールの仕様は以下のとおりです。

  • wonderコマンドを実行すると、文字列「What a wonder!」を標準出力する。

以上です。あくまでサンプルなので、深い意味はありませんし実用性もありません:)

前提

Pythonのバージョンは2.7系を想定しています。

個人的には一刻も早くPython3が普及してもらいたいのですが、 ツールとして配布する際は広く普及しているバージョンに合わせるのがよいでしょう。

また、インストール方法はpipによるGitリポジトリからのインストールとします。
社内や自分自身のみで利用する分には、これが一番手っ取り早いと思います。1

事前準備

イマドキの開発環境では、処理系のある特定のバージョンを手軽にインストールするためのユーティリティを使うことが多いです。
システムインストールされている処理系と、開発用途の処理系と明確に分離することで、
「開発するために入れた処理系では自分のお気に入りのツールが動かない」などという事故が無くなります。

Rubyならばrbenv、Pythonならばpyenvがそれに該当します。

以下のコマンド一発で、指定したバージョンをインストールできます。楽ちんですね!

$ pyenv install 2.7.5  

また、外部パッケージも同様に、システムインストールされている処理系に対してグローバルにインストールされると、「あるパッケージの特定のバージョンに暗黙の依存関係が生まれる」などの事故が発生する可能性があります。 2
それを防止するために、特定のディレクトリや仮想環境などの隔離された環境にパッケージをインストールするツールを使用します。

RubyならばBundler, Pythonならばvirtualenvなどがサポートしています。3

さて、今回の例ではpyenvと、そのプラグインであるpyenv virtualenvを使用します。個人的に、virtualenv単体で使うよりも簡単だと思います。

各ツールのインストールは上記URLから参照してください。 インストール後、以下のコマンドを実行することで、仮想環境が作成できます。

$ pyenv virtualenv 2.7.5 wonder_tool_dev  

仮想環境作成後、その仮想環境を使用したいディレクトリで以下のコマンドを実行することで、
そのディレクトリ配下では指定した仮想環境の処理系・ライブラリを参照します。

$ pyenv local wonder_tool_dev  

実装編

ディレクトリ構成

まず、以下のようなディレクトリ構成を作成します。

python_cli_sample/  
  |  
  |-- wonder_tool/  
  |     |-- __init__.py  
  |     |-- main.py  
  |  
  |-- setup.py  
  |-- requirements.txt  

ルートディレクトリpython_cli_sampleが、このCLIツールのプロジェクトルートです。
そのサブディレクトリwonder_toolは、このツールのPythonパッケージのルートとなります。

setup.py

プロジェクトルート配下のsetup.pyは、このwonder_toolをインストールするために必要なセットアップスクリプトです。
以下に例を載せます。

  
from setuptools import setup, find_packages  
  
setup(  
    name='wonder_tool',  
    version='1.0',  
    packages=find_packages(),  
    install_requires=['cliff'],  
    entry_points={  
        'console_scripts':  
            'wonder = wonder_tool.main:wonder_main'  
    },  
    zip_safe=False,  
    classifiers=[  
          'Environment :: Console',  
          'Intended Audience :: Developers',  
          'Operating System :: MacOS :: MacOS X',  
          'Programming Language :: Python',  
          'Programming Language :: Python :: 2.7',  
    ],  
)  

詳細は公式ドキュメントを参照していただくとして、 ここでは実行可能なパッケージとして配布する際に必要なconsole_scriptsに注目してください。

entry_points={  
    'console_scripts':  
        'wonder = wonder_tool.main:wonder_main'  
},    

この記述によって、pipでインストール後に「wonder」コマンドを使用すると wonder_tool.mainモジュールのwonder_main関数が実行できるようになります。

もう一点、重要なのが「install_requires」です。 これは配布するパッケージが依存する外部ライブラリを指定します。

    install_requires=['cliff'],  

これにより、pipでwonder_toolをインストールした際に、依存するcliffというCLIフレームワークも一緒にインストールされます。
今回のサンプルではcilffは使用せずにCLIツールを作成しますが、複雑なCLIツールを作成する場合はcliffのようなフレームワークの使用を検討した方が良いでしょう。

注意点ですが、install_requiresには動作に関するライブラリ以外は含めないようにしましょう。
例えば、py.testやfactory-boyなどのテスト用ライブラリはツール自体の動作に関するライブラリではないため、install_requiresに含めてはいけません。
ただツールを利用したいだけなのに、余分なライブラリのインストールを強制するのは、一般的に良いことではありません。

requirements.txt

開発時にのみ必要なライブラリは、別途requirements.txtに含めるようにします。

requirements.txtの詳細はこちらを参照してください。
以下のコマンドで現在インストールされているパッケージを出力できるので、参考にすると良いでしょう。

$ pip freeze  

開発中は、以下のコマンドでsetup.pyのinstall_requiresおよびrequirements.txtに記述されている依存ライブラリをインストールします。

$ pip install -e .  
$ pip install -r requirements.txt  

実装例

では、実際の実装です。下記のwonder_main関数をコマンドで呼び出すことになります。

python_cli_sample/wonder_tool/main.py

# -*- coding: utf-8 -*-  
import sys  
  
def wonder_main(argv=sys.argv[1:]):  
    print 'What a wonder!'  


配布編

CLIツールの配布はpipで行います。pipは、GitやMercurial,SubversionなどのVCSのリポジトリをパッケージリポジトリとして扱えるので、それを利用します。

Gitリポジトリへのパッケージ公開

実装したwonder_toolをGitリポジトリにPushします。
この手順は、普段のGitの利用方法と全く同じです。5

$ git init  
$ git add .  
$ git commit -m "Initial commit"  
$ git remote add origin https://github.com/Wonderplanet-Engineer/python_cli_sample.git  
$ git push -u origin  

pipでインストール

クライアントの動作環境をエミュレートするために、pyenv + virtualenvを再び使用します。

$ pyenv virtualenv 2.7.5 wonder_tool_test  
$ pyenv local wonder_tool_test  

カレントディレクトリにwonder_tool_testという隔離された環境が作成できました。
実際にpipでGitリポジトリからパッケージをインストールしてみます。

$ pip install git+https://github.com/Wonderplanet-Engineer/python_cli_sample  

これで「wonder」コマンドが使えます!
・・・と思いきや、pyenvでのエミュレートにはもう一手間あります。 以下のコマンドを実行し、インストール済みのコマンドのsymlinkを作成し直してください。

$ pyenv rehash  

pyenvを使用していない環境では、「pyenv rehash」を実行する必要はありません。 それでは、実際に「wonder」コマンドを実行してみましょう。

python_cli_sample

できました!

少し面倒くさいところもありますが、この手順を踏むことによって
クライアントに対しツールをパッケージとして配布することができます。

今回の「wonder_tool」はGithub上のリポジトリに公開していますので、
試しにpipでインストールしたり、forkして拡張したりしてみてください。


  1. インストールする側にGitが必要です。Github for MacでCLIツールをインストールするのがお手軽です。

  2. 特にRubyは注意したほうが良いです。activesupport特有のメソッドが標準ライブラリに付属していると勘違いしている人は思った以上に多いです。

  3. これらの2種類のツールを使いこなすのは、ほぼ必修といっていいほど。ただRubyでプログラミングできるだけでは、Rubyエンジニアとは言えないでしょう。

  4. 作者はpyenvと同じ方らしいので安心です。

  5. Gitを普段どのように扱うかは、筆者の過去のエントリを参考にしてみてください。