カイワレの大冒険 Third

技術的なことや他愛もないことをたまに書いてます

Pythonでコマンドラインツールを作りたいときには、setup.pyには何を書くべきか

前回のPython製コマンドラインツールのディレクトリ構成について。その考察。の続きです。 はてブコメントやFBでのコメントでsetup.pyに関する言及がいくつかあったので、どのようなことを書いたほうがいいのか調べてみました。

setup スクリプトを書くやいくつかのソースコード読んでみた範囲ですが、前回の記事のsetup.pyを見直したくなったので、まとめておきます。

前回の記事で書いたsetup.pyは以下。

import os
from distutils.core import setup

setup(name='SAMPLE_PROJECT',
      version='1.0.0'
      )

正直ちゃんと理解してなく、かなり雑でした。ので、色々見なおした結果以下のようなものに。

from setuptools import setup
import sys
from SAMPLE_PROJECT import (
    __author__,
    __author_email__,
    __app__,
    __version__,
    __release__,
    __prog__
)

# validation
if sys.version_info < (3, 4):
    print("Building SAMPLE_PROJECT requires at least Python 3.4 to run.")
    sys.exit(1)

setup(name=__app__,
      version= "{}.{}".format(__version__, __release__),
      description='hogehoge tool',
      long_description="""this supports to hogehoge.""",
      author=__author__
      author_email=__author_email__
      url='https://github.com/masudak/hogehoge',
      packages=[
          'SAMPLE_PROJECT' # __init__.pyを置いてあるので、これでモジュール化できる
          ],
      py_modules=[
          # 上記のpackagesで収まるので、特に使わない
          ],
      zip_safe = (sys.version>="2.5"), #2.5の頃はディレクトリとしてegg作らないとまずかった?
      scripts=[
          'bin/command1',
          'bin/command2'
          ],
      license='GNU Lesser General Public License v3 or later (LGPLv3+)',
      keywords='',
      platforms='Linux',
      classifiers=['Intended Audience :: System Administrators',
                   'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)',
                   'Natural Language :: Japanese',
                   'Programming Language :: Python :: 3.4',
                   ],
      )

一つ一つ説明していきます。

まず、以下。

from setuptools import setup
import sys
from SAMPLE_PROJECT import (
    __author__,
    __author_email__,
    __app__,
    __version__,
    __release__,
    __prog__
)

1行目ですが、現在はdistutilsではなく、setuptools使ったほうがいいですね。歴史的経緯とか知らなかったので勉強になりました。とりあえず今はsetuptoolsを使っておくのが無難のようです。 詳しく知りたい方はpythonパッケージ管理についてのまとめなどを読むとよいかと思います。 また、authorなどの必要な情報をSAMPLE_PROJECT/init.pyに以下のように書きます。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
__author__ = 'Kenichi Masuda'
等々
__version__ = '1.0'
__release__ = '1'

あとはimportできるので、それをsetup.pyのなかで使う感じですね。 人によっては、info.pyのようなファイルをトップディレクトリにおいて、それに情報まとめている人もいました。それぞれやってみるとどちらが使いやすいとかわかってきそうです。

次にこれ。

# validation
if sys.version_info < (3, 4):
    print("Building SAMPLE_PROJECT requires at least Python 3.4 to run.")
    sys.exit(1)

他の人のsetup.pyにあったのですが、バリデーションをしていました。 僕のスクリプトは3.4しかテストしてないので、こうやっておけばバリデーションになるなと。

そして、setup関数内ですね。

setup(name=__app__,
      version= "{}.{}".format(__version__, __release__),
      description='hogehoge tool',
      long_description="""this supports to hogehoge.""",
      author=__author__
      author_email=__author_email__
      url='https://github.com/masudak/hogehoge',

上述したinit.pyの情報とかを使って、項目を埋めていきます。 ここは特に難しくないでしょう。

次に以下。

      packages=[
          'SAMPLE_PROJECT' # __init__.pyを置いてあるので、これでモジュール化できる
          ],
      py_modules=[
          # 上記のpackagesで収まるので、特に使わない
          ],
      zip_safe = (sys.version>="2.5"), #2.5の頃はディレクトリとしてegg作らないとまずかった?
      scripts=[
          'bin/command1',
          'bin/command2'
          ],

scriptsでコマンドを書いておくと「python setup.py install」したときにパスが通ったコマンドになります。 なので、「pip install -e .」とかをせずとも、コマンドとして正しく使えるようになるわけです。 ただ、そのために下準備としてpackagesにSAMPLE_PROJECTを加えておかないとutil.pyなどが使えません。他にもimportするパッケージがあれば記述は必要になります。

ちなみにはてブコメントで、「setup(entry_points={'console_scripts': ["your_CMD"]})」はどうかというコメントがありましたが、もしそうするとなると、setup.pyは以下のようになるかと。

setup(name=__app__,
      packages=['bin' # console_scriptsでbinをmoduleとして認識できるようにする
          ],
      entry_points={'console_scripts': [
              "command1 = bin.command1:main"]
          },
      )

あくまで個人的な意見ですが、これだとbin.command1:mainというようにメソッド名を記さないといけないのですよね。そういう設計で書いてなかったので、これで頑張るよりは上記のscriptsのほうが楽だったので、そうしています。もしかしたら、何か便利な方法が他にあるのかもしれないので、そういう方法あったらご指摘お願いします。

また、packagesにすべて記述しているため、特にpy_modulesで指定することはありません。特定のファイルだけパッケージ化したい場合とかはpy_modules必要かもですね。 加えて、zip_safeですが、3系しか対象にしてなければ、記述はそもそもいらないかもですね。今はデフォルトTrueなので。ただ、2.4もサポートしていた人が上記のような記述をしていたので、参考のために記述しておきました。

そして、最後に以下。

      license='GNU Lesser General Public License v3 or later (LGPLv3+)',
      keywords='',
      platforms='Linux',
      classifiers=['Intended Audience :: System Administrators',
                   'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)',
                   'Natural Language :: Japanese',
                   'Programming Language :: Python :: 3.4',
                   ],
      )

ここは特に説明は必要ないかもですね。当てはまるように記述しておけばよいかと。classifiersは一覧ページで探せばよいかと。

終わりに

まとめてみましたが、こうやって書いてみるとパッケージを配布するできるぐらいまで詳細に書いておいたほうがいいですね。特に今までは pip install -e . でテスト的に動かしていたものが、 python setup.py install になることで正しくインストールされますし、eggとして配布も可能になります。 python bin/command1 として実行しなくても、 command1 で実行できるわけで。なので今後は正しく配布できるようsetup.pyも充実させていこうと思った次第です。勉強になった。