setup.pyの拡張
pythonのプログラムを書いたらsetup.pyでインストールできるようにしよう!ということで書いたけどMakefileのがよくね?とかそんな疑問を持ちつつほげー
やりたいこと
- 適当なコマンドの追加
- setup()のattrs引数で、このコマンドの対象とするファイルを指定する
- buildを実行すると、追加したコマンドを実行する
- cleanを実行すると、追加したコマンドの生成したファイルを削除する
まず、コマンドの追加方法。distutils.cmd.Commandを派生させる。
from distutils.cmd import Command class MyCommand(Command): # 以下のクラス変数は宣言しないと怒られる description = 'My command' user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): print('Do my command.')
initialize_option, finalize_options, runをoverwrideする。実行したい処理はrunに記述する。その上で、setup()のcmdclassにコマンド名とともに作成したクラスを指定する。
from distutils.core import setup setup( cmdclass = {'mycommand': MyCommand} )
以上でmycommandが実行可能に
$ ./setup.py mycommand running mycommand Do my command.
つぎに、コマンドの中からsetup()に独自の引数を追加し、MyCommandの中から参照する。具体的には以下のように、greetingを指定したらその内容を出力するようにしてみる。
setup(cmdclass = {'mycommand': MyCommand }, name = 'hoge', version = '1.0.0', packages = [ 'hoge' ], greeting = 'Popopopoooon' )
まず、Distributionを派生させて、インスタンス変数を追加する。ここでの注意は、スーパークラスの__init__()を呼ぶ前にインスタンス変数を作っておくこと。つまり、スーパークラスの__init__()でそれっぽい処理が走ってる。
from distutils.dist import Distribution class MyDistribution(Distribution): def __init__(self, attrs=None): self.greeting= None Distribution.__init__(self, attrs)
次に、MyCommandでそれを取得する。
from distutils.cmd import Command class MyCommand(Command): # 以下のクラス変数は宣言しないと怒られる description = 'My command' user_options = [] def initialize_options(self): self.greeting = None def finalize_options(self): self.greeting = self.distribution.greeting def run(self): print(self.greeting)
で、このクラスをsetup()のdistclassに指定。
setup(distclass = MyDistribution, cmdclass = {'mycommand': MyCommand }, name = 'hoge', version = '1.0.0', packages = [ 'hoge' ], greeting = 'Popopopoooon' )
実行!
$ ./setup.py mycommand running mycommand Popopopoooon
最後に以下の2つ
- buildを実行すると、追加したコマンドを実行する
- cleanを実行すると、追加したコマンドの生成したファイルを削除する
やってることは同じで、デフォルトのコマンドのrun()を上書きしている。
from distutils.command.build import build from distutils.command.clean import clean class MyBuild(build): def run(self): self.run_command('mycommand') return build.run(self) class MyClean(clean): def run(self): clean.run(self) if self.all: print('Remove nanka')
MyBuildではself.run_command()でmycommandを実行している。
また、MyCleanでは一応、--allオプションが指定されたときだけ処理が走るように、self.allを見ている。
で、最後にsetup()のcmdclassに作ったクラスを指定する。
setup(distclass = MyDistribution, cmdclass = {'mycommand': MyCommand, 'build': MyBuild, 'clean': MyClean }, name = 'hoge', version = '1.0.0', packages = [ 'hoge' ], greeting = 'Popopopoooon' )
実行!
$ ./setup.py build running build running mycommand Popopopoooon running build_py $ ./setup.py clean --all running clean removing 'build/lib.linux-x86_64-2.6' (and everything under it) 'build/bdist.linux-x86_64' does not exist -- can't clean it 'build/scripts-2.6' does not exist -- can't clean it removing 'build' Remove nanka
しかし、makeから劣化してねーかなぁ…