2007年11月9日

PasteScriptでコマンドを作る


PylonsがベースとしているPasteは、サブモジュールにPasteScriptってのがあって、PylonsのrestcontrollerとかcontrollerなどもPasteScriptコマンドなのだね。



で、Pylons自体には、いまのところコントローラー生成のコマンドはあるのだけど、モデル生成のコマンドがない。
勉強がてら、モデル生成のコマンドを作ってみる。



model.py_tmpl

from elixir import *

class ${classname}(Entity):
""" ${classname}
"""

非常にいい加減なテンプレート。
クラス定義だが、中身を決められないので、Entityを継承したクラスってことくらいしか書けない。
まあ、いいか。



PasteScriptには、filemakerというモジュールがあって、ファイル生成処理に必要なことを色々やってくれるFileOpというクラスがある。



FileOpは生成するときに、テンプレートのありかを指定しておく。
今回は、commands.py内にModelCommandクラスもって、相対パスtemplates内に、上記のmodel.py_tmplを配置した。



このときFileOpの生成はこんな感じ

file_op = FileOp(source_dir=os.path.join(os.path.dirname(__file__), 'templates')



んで、テンプレート内で展開される変数もfile_opに持たせてあげる。

file_op.template_vars.update({'classname':'Spam'})



テンプレートから実際のファイルを生成するには、copy_fileメソッドを使う。

file_op.copy_file(template='model.py_tmpl',
dest='model',
filename='spam')

.pyの拡張子は自動で付けてくれるっぽい。
destは、ファイル生成先のディレクトリだけど、プロジェクトパッケージからの相対パスになるみたいだ。
(hogeプロジェクトなら、hoge/model 以下に生成される)



結局は、このあたりのFileOpオブジェクトに対する操作を、コマンドの中でやればいい。



PasteScriptコマンドは、paste.script.command.Commandクラスを継承をする。
また、クラス属性で、引数の数や、オプション、コマンドの説明など、メタ属性を設定する。

from paste.script import command
class ModelCommand(command.Command):
max_args = 1
min_args = 1

usage = "paster model model_name"
summary = "Create a set of REST Controller, Model and Templates."
group_name = "roadcones"

parser = command.Command.standard_parser(verbose=True)




コマンドが実行されたときは、commandメソッドが呼ばれる。
commandメソッドをオーバーライドして、先ほどのFileOpを操れば、とりあえずコード生成ができる。

def command(self):
file_op = FileOp(source_dir=os.path.join(
os.path.dirname(__file__), 'templates'))

member_name = self.args[0]

model_name = util.class_name_from_module_name(member_name)
file_op.template_vars.update(
{'classname':model_name
}
)
file_op.copy_file(template='model.py_tmpl',
dest='model',
filename=member_name)


args属性に、コマンドライン引数が入ってるので、1つの引数を受け取っておく。
この引数はモデルのファイル名とクラス名に用いる。
FileOpオブジェクトの使い方は上述の通り。
また、util.class_name_form_module_nameは、ファイル名からクラス名に変換するもの。
pylons.utilを使っている。



さてこのクラスをPasteScriptコマンドに登録しないと使えない。
setup.pyのentry_pointsに以下のように追加しておく。
(以下のソース例では、このコマンドをroadconesプロジェクトで作ってるものとしています。)

[paste.paster_command]
resource = roadcones.commands:ModelCommand

setup.pyを変更したら、egg_infoを更新しときましょう。

$ python setup.py egg_info



ちなみに、setup.py develop でegg_infoを登録しておくと、編集中のソースツリーがPYTHON_PATHに追加されるので、便利です。



あとは、このコマンドを使いたいプロジェクトで、egg_info/paster_plugins.txt内に、コマンドを定義しているegg名を記述しておくとコマンドを使えるようになります。
paster_plugins.txtの例

Pylons
WebHelpers
roadcones
PasteScript



実際には、paster_pluginsに追加する処理は、PasteScriptのプロジェクトテンプレートでやることになるでしょう。



参考