Zopeを対話モードで起動する
Pythonの対話モードではZopeのプロダクトをimportしたりZopeに格納されているオブジェクトを調べたりすることは簡単にはできません。Zopeにはこういった目的のための対話モードがあります。この昨日はZope2.7以降で動作しますが、zopectlコマンドを使用するため残念ながらWindowsでは動作しません。
とりあえず実行してみる
実行にはzopectlコマンドを使用します。実行対象のzope instanceディレクトリで以下のように実行します。
$ bin/zopectl debug
### console messages... ###
>>>
これで、このinstanceにインストールされているプロダクトやZopeの環境が全て読み込まれた状態で対話モードになりました。この時点でローカル名前空間にいくつかの変数が用意されています。
['Zope2', 'configure', '__builtins__', 'app', '__name__', '__doc__']
ここでZMIのrootにあたるのが app オブジェクト(Zopeのルートフォルダのインスタンス)です。フォルダインスタンスに格納されているオブジェクトの一覧は objectIds() メソッドを使って取得し、辞書のようにアクセスすることが出来ます。
['acl_users', 'Control_Panel', 'temp_folder', 'session_data_manager', 'browser_id_manager', 'error_log', 'index_html', 'standard_error_message', 'standard_html_footer', 'standard_html_header', 'standard_template.pt', 'virtual_hosting']
>>> app['acl_users']
<UserFolder at /acl_users>
>>> app['acl_users'].user_names()
['admin']
他にもobjectValues()やobjectItems()などのメソッドがあります。詳しくはZopeのHelpでAPIリファレンスを参照してください。
データベースの内容を更新する
ユーザーの追加を例にデータベースの更新をしてみます。通常はZMIでユーザー追加しますが、zopectl debug中はPythonのプログラムを書くのと同等の事ができます。このためユーザー追加や削除、フォルダ追加、などなど、プログラムを書いて出来ることは全て実行することが出来ます。
ではまずはユーザーを作成してみましょう。とりあえずユーザーを実装しているclassが分からないので、既存のユーザーから情報を引き出して調べるところから始めてみます。
<User 'admin'>
>>> acl.getUser('admin').__class__
<class 'AccessControl.User.User'>
>>> from AccessControl.User import User
>>> help(User.__init__)
Help on method __init__ in module AccessControl.User:
__init__(self, name, password, roles, domains) unbound AccessControl.User.User m
ethod
>>> me = User('me', 'pw', [], '')
>>> me
<User 'me'>
これでUserオブジェクトが出来ました。上記のようにhelp関数を使うと、クラスやメソッドに記述されているdoc stringを表示することが出来ます。このあたりはPython標準の仕組みですので、Zopeに限らずPythonコードを書くときには参考になると思います。また、自作プログラムを書く場合にdoc stringをちゃんと書いておけば上記のようにヘルプとして参照できるようになります。
次は、これをacl_usersフォルダに格納してやれば良いわけですが、それらしいメソッドやdoc stringが見あたりません。そこでtestコードを参照してみます。 Zope-2.9.6/lib/python/AccessControl/tests/testUserFolder.py を見ると、 _doAddUser メソッドでユーザーを追加していることが分かりました。ということで先ほど作成したユーザーの事は忘れて _doAddUser を使ってユーザーを追加することにします。
>>> acl.user_names()
['admin', 'me']
追加できたようです。
ここで、このまま対話モードを終了させるとデータベースにはユーザー追加は反映されません。このため、対話モードでどんなに破壊的は操作を行っても再起動すればやりなおしが出来ます。
上記の操作をデータベースに保存するためには以下のコードを実行する必要があります。
>>> transaction = get_transaction()
>>> transaction.commit()
Zope2.8.x, 2.9.x, 2.10.x
>>> import transaction
>>> transaction.commit()
逆に、それまでの操作を破棄する場合は transaction.abort() を呼び出します。
開発に応用する
前述のようにユーザー追加の方法を調べたり、フォルダの中身について調べたりする他に、開発中のコードをimportして関数を呼び出したり、他のプロダクトのAPIを順次呼び出してみることができます。これを従来の開発手順と比較してみました。開発手法の一つとして参考にしてください。
- 従来の開発手順
- ソースファイルにソースコードを書く
- Zopeを再起動(プロダクトをリロード)する
- ZMIなどから操作する
- エラーが発生したり、修正したい要素を見つける
- ソースコードを修正する(以下繰り返し)
- zopectl debugを使った開発
- ソースファイルにソースコードを書く
- zopectl debugで起動する
- 対話モードでプロダクトの関数を呼び出す
- エラーが発生したり、修正したい要素を見つける
- 対話モードで修正案コードを書いて実行する
- 目指すコードが出来る
- 対話コンソールの内容をコピーしてソースファイルに反映する
テストコードに応用する
zopectl debugを使った開発では、主にZopeや他のプロダクトのAPIを呼び出すコードを書くことになるでしょう。同様に、対話コンソールで自分で作ったプロダクトの関数を呼び出すコードを書いてみましょう。たとえば以下のような関数を持つプロダクトがあるとします。
users = {}
def doAddUser(self, name, password):
user = User(name, password)
self.users[name] = user
def getUser(self, name)
return self.users[name]
def user_names(self)
return self.users.keys()
これらの実装を実際に使う側の立場で、対話コンソールで関数を呼び出してみます。
>>> uf = UserFolder
>>> uf.doAddUser('me', 'pw')
>>> uf.user_names()
['me']
>>> uf.getUser('me')
<User 'me'>
これで先ほどのコードのUserFolderクラスの機能の一部が正しく動いていることが証明できました。この対話コンソールの内容をコピーして、UserFolderクラスのdoc stringとして貼り付けます。
"""
UserFolder hold users.
>>> from MyProduct.UserFolder import UserFolder
>>> uf = UserFolder
>>> uf.doAddUser('me', 'pw')
>>> uf.user_names()
['me']
>>> uf.getUser('me')
<User 'me'>
"""
このようにdoc stringに対話コンソールの内容を記述しておくと、doctestとして実行できるようになります。DocTestについて詳しくは unit testing を参考にしてください。
This How-to applies to: Zope 2.10.x, Zope 2.9.x, Zope 2.8.x, Zope 2.7.x