何らかのコンピュータプログラムを使いたいと思って,インストール手順が煩雑だと,他のものを使おうとなるのが人の性だと思う.なので自作のPythonプロジェクトにおいても,できるだけ楽にパッケージをインストールできるようにしておきたい.そのためには,setup.pyを用意して,pipを用いたインストールを可能にするのがよいと思われる.

setup.pyの導入

https://github.com/kennethreitz/setup.py を参考にして,プロジェクトディレクトリにおいて,次のコマンドにて setup.py のテンプレートをコピーしてくる.

$ curl -O https://raw.githubusercontent.com/navdeep-G/setup.py/master/setup.py

中身を少々変更するとある程度出来上がる.

どのpythonソースやディレクトリをパッケージとするかは,次の部分で指定されている.

    packages=find_packages(exclude=["tests","*.tests","*.tests.*","tests.*"])

find_packages() は __init__.py の存在するディレクトリを探す.

コマンドのインストール

コンソールで使えるコマンドをインストールしたいと思うと,どこかのモジュールの関数名を指定することになるが,これだとdocoptで引数を取ってくることができない? そんなことはなく,関数をコマンドラインから読んだとしても,sys.argv変数にはそのときにつけたオプションが入っており,

args = docopt(__doc__)

としたとき,docoptはデフォルトとしてオプションとして argv=sys.argv[1:] を取ってくる.

もしコマンド名をpythonファイル名と異なる名前で呼び出すとしたら,docstring内の実行ファイル名とコマンド名が異なることになる.その場合,次のようにすれば解決できる.

"""
This is the docstring...
 
Usage:
    {0:s} [options] FILE
 
Options:
    -h     Help message.
"""
import os, sys
from docopt import docopt
 
def main():
    args = docopt(__doc__.format(os.path.basename(sys.argv[0])))
    ...
 
if __name__ == "__main__":
    main()
  • Usage内のコマンド名のところを {0:s} と書いておき,後で代入されるように書いておく.
  • pythonファイルを実行した場合に main() 関数が呼ばれるようにしておく.
  • main() 関数内で docopt() を呼ぶ.
  • docopt() の引数で,上記のように,sys.argv[0]のファイル名部分のみを __doc__ に渡す.

こうすることで,ファイルを実行した場合と,別名コマンドでこのファイルの main() 関数を読んだ場合に,自動的に適切な名前が入る.

pyenvを使っている際のパッケージのインストール

pyenvを使ってpythonをインストールしてバージョンを切り替えたりして使っている場合,

$ python setup.py install

などとしてインストールしてもすぐには ~/.pyenv/shims/ にコマンドがインストールされない.(モジュールだけなら問題なくインストールされるが...)そのため,setup.pyでインストールするのではなく,pipを利用し,

$ python setup.py sdist
$ pip install -e .

とする.-eオプションは,editableモードとよばれるもの.ココ参照.このモードでインストールすると,実際のファイルは元々の場所にあり,pythonのlib/site-packages/の方にはそこへのリンクが生成されるので,元のファイルを変更するとそれが反映される.なので開発段階のパッケージはこれでインストールしておいて開発するが良さそう(そういうためのモードと思われる).