distributeのextra_commandの作り方
pythonのsetup.pyを使って以下のように実行可能なコマンドを作ってみる
$ python setup.py <command name> <args> ...
setup.py
pythonのパッケージはsetup.pyを持っている。
このsetup.pyは提供されるパッケージに関するコマンドを持っている。
例えば、以下のような機能がある。
- packageをpypiにuploadしたり
- テストランナーを走らせたり
- 配布用のバイナリを作ったり
extra_command
pythonのsetup.pyが提供するコマンドを自分で作成することができる。
利用できるコマンドは以下のようにすると分かる。
# $ ls | grep setup.py # setup.py $ python setup.py --help-command # Standard commands: # ... # # Extra commands: # rotate delete older distributions, keeping N newest files # develop install package in 'development mode' # setopt set an option in setup.cfg or another config file # saveopts save supplied options to setup.cfg or other config file # egg_info create a distribution's .egg-info directory # nosetests Run unit tests using nosetests # alias define a shortcut to invoke one or more commands # easy_install Find/get/install Python packages # bdist_egg create an "egg" distribution # install_egg_info Install an .egg-info directory for the package # test run unit tests after in-place build # # usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] # ...
rotateとかdevelopとか定義されている。
自分でコマンドを追加してみよう
行う手順は以下のとおり
- entry_pointに追加する
- 呼ばれるコマンドを作成
entry_pointに追加する。
setup.pynのetnry_pointに追加する。
今回のディレクトリ構成は以下のとおり。foo/scripts.pyに追加する予定
. ├── foo │ ├── __init__.py │ └── scripts.py ## ここにfoo_commandを作成 │ └── setup.py
setup.pyにentry_pointsを追加.(scripts.pyのfoo_command)
setup( # ... entry_points = { "distutils.commands": [ "foo = foo.scripts:foo_command", ], }, )
commandを作成する
(試しに関数を書いてみる)
# scripts.py def foo_command(*args, **kwargs): pass
このパッケージをインストールしたときfooコマンドが使えるようになれば良い
$ python setup.py develop $ python setup.py --help-commands | grep foo # foo (no description available) ## あった。実行してみる $ python setup.py foo # .. # TypeError: issubclass() arg 1 must be a class
実行できたけど、エラーで落ちた。次は真面目に実装してみる
真面目にコマンドを実装
調べてみると、distributeのextra_commandsは以下の要件を満たしている必要があるらしい。
- setuptools.Commandのsubclass
- run,initialize_options,finalize_optionsを実装
- その他細かな事
- user_optionsでオプショナル引数の指定
- (末尾が=のものは引数をとる)
- command_consumes_arguments=Trueにすると引数を受け取れるようになる。
- user_optionsでオプショナル引数の指定
from setuptools import Command class foo_command(Command): description = "echo foo" ## user_option = [(long, short, help-message), ....] # e.g. # long :: "verbsoe" # short :: "v" # help :: "verbose output" # user_options = [("user=", "u", "echo user")] command_consumes_arguments = True def run(self): print self.args print "user: %s foo" % self.user def initialize_options(self): self.args = [] self.user = None def finalize_options(self): pass
実行可能になった。
$ python setup.py foo -u xxx 1 2 3 4 # running foo # ['1', '2', '3', '4'] # user: xxx foo $ python setup.py foo 1 2 3 4 # running foo # ['1', '2', '3', '4'] # user: None foo
appendix コマンドにaliasを付ける。(setup.cfgでデフォルトの設定を付加する)
ついでに、setup.cfgを書いてdefaultの設定を付加してみることにする。
ここでは、上で作ったfooコマンドの-uの値を"User"に設定してみる。
setup.pyで結びつけたコマンド名をセクションにして設定を記述する。
#setup.cfg [foo] user = "User"
こうすると、python setup.py fooを実行したとき、デフォルトで-u "user"が渡された状態になる。
$ python setup.py foo 1 running foo ['1'] user: "User" foo