August 31, 2009

Using pyjamas on Kay

I use ${PYJAMAS_HOME} and ${PROJECT_DIR} variable for explanation here. ${PYJAMAS_HOME} is a directory in which you installed pyjamas. ${PROJECT_DIR} is your project directory.

For opener, please begin with customizing ${PYJAMAS_HOME}/examples/jsonrpc. You need only JSONRPCExample.html in the `public` directory.


$ tree kay-pyjamas-sample
kay-pyjamas-sample
|-- JSONRPCExample.py
`-- public
`-- JSONRPCExample.html

1 directory, 2 files


Edit your own JSONRPCExample.py as follows:

import pyjd # dummy in pyjs

from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.TextArea import TextArea
from pyjamas.ui.Label import Label
from pyjamas.ui.Button import Button
from pyjamas.ui.HTML import HTML
from pyjamas.ui.VerticalPanel import VerticalPanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.ListBox import ListBox
from pyjamas.JSONService import JSONProxy

class JSONRPCExample:
def onModuleLoad(self):
self.TEXT_WAITING = "Waiting for response..."
self.TEXT_ERROR = "Server Error"
self.METHOD_ECHO = "Echo"
self.METHOD_REVERSE = "Reverse"
self.METHOD_UPPERCASE = "UPPERCASE"
self.METHOD_LOWERCASE = "lowercase"
self.methods = [self.METHOD_ECHO, self.METHOD_REVERSE, self.METHOD_UPPERCASE, self.METHOD_LOWERCASE]

self.remote_py = EchoServicePython()

self.status=Label()
self.text_area = TextArea()
self.text_area.setText("""{'Test'} [\"String\"]
\tTest Tab
Test Newline\n
after newline
""" + r"""Literal String:
{'Test'} [\"String\"]
""")
self.text_area.setCharacterWidth(80)
self.text_area.setVisibleLines(8)

self.method_list = ListBox()
self.method_list.setName("hello")
self.method_list.setVisibleItemCount(1)
for method in self.methods:
self.method_list.addItem(method)
self.method_list.setSelectedIndex(0)

method_panel = HorizontalPanel()
method_panel.add(HTML("Remote string method to call: "))
method_panel.add(self.method_list)
method_panel.setSpacing(8)

self.button_py = Button("Send to Python Service", self)

buttons = HorizontalPanel()
buttons.add(self.button_py)
buttons.setSpacing(8)

info = """

JSON-RPC Example


This example demonstrates the calling of server services with
JSON-RPC.


Enter some text below, and press a button to send the text
to an Echo service on your server. An echo service simply sends the exact same text back that it receives.

"""

panel = VerticalPanel()
panel.add(HTML(info))
panel.add(self.text_area)
panel.add(method_panel)
panel.add(buttons)
panel.add(self.status)

RootPanel().add(panel)

def onClick(self, sender):
self.status.setText(self.TEXT_WAITING)
method = self.methods[self.method_list.getSelectedIndex()]
text = self.text_area.getText()
id = -1
if method == self.METHOD_ECHO:
id = self.remote_py.echo(text, self)
elif method == self.METHOD_REVERSE:
id = self.remote_py.reverse(text, self)
elif method == self.METHOD_UPPERCASE:
id = self.remote_py.uppercase(text, self)
elif method == self.METHOD_LOWERCASE:
id = self.remote_py.lowercase(text, self)
if id<0:
self.status.setText(self.TEXT_ERROR)

def onRemoteResponse(self, response, request_info):
self.status.setText(response)

def onRemoteError(self, code, message, request_info):
self.status.setText("Server Error or Invalid Response: ERROR %d - %s" %
(code, message))


class EchoServicePython(JSONProxy):
def __init__(self):
JSONProxy.__init__(self, "/json", ["echo", "reverse", "uppercase", "lowercase"])

if __name__ == '__main__':
# for pyjd, set up a web server and load the HTML from there:
# this convinces the browser engine that the AJAX will be loaded
# from the same URI base as the URL, it's all a bit messy...
pyjd.setup("http://127.0.0.1/examples/jsonrpc/public/JSONRPCExample.html")
app = JSONRPCExample()
app.onModuleLoad()
pyjd.run()


Then, cd into this directory and compile it. This time, I use ${PROJECT_DIR}/media for a target directory.


$ cd kay-pyjamas-sample
$ ${PYJAMAS_HOME}/bin/pyjsbuild -o ${PROJECT_DIR}/media JSONRPCExample.py


If succeed, you'll get ${PROJECT_DIR}/media/JSONRPCExample.html. Please access http://localhost:8080/media/JSONRPCExample.html and, you'll get forms for testing jsonrpc.

Let's write server side code. First, you need to edit urls.py in your application.

${PROJECT_DIR}/myapp/urls.py

# -*- coding: utf-8 -*-
# myapp.urls


from werkzeug.routing import (
Map, Rule, Submount,
EndpointPrefix, RuleTemplate,
)
import myapp.views

def make_rules():
return [
EndpointPrefix('myapp/', [
Rule('/', endpoint='index'),
Rule('/json', endpoint='json_rpc'),
]),
]

all_views = {
'myapp/index': myapp.views.index,
'myapp/json_rpc': myapp.views.json_rpc,
}


You also need to edit views.py.

${PROJECT_DIR}/myapp/views.py

# -*- coding: utf-8 -*-
# myapp.views
# ... (snip)

import simplejson

# ... (snip)

def json_uppercase(args):
return [args[0].upper()]

def json_echo(args):
return [args[0]]

def json_reverse(args):
return [args[0][::-1]]

def json_lowercase(args):
return [args[0].lower()]

def json_rpc(request):
if request.method:
args = simplejson.loads(request.data)
method_name = 'json_%s' % args[u"method"]
json_func = globals().get(method_name)
json_params = args[u"params"]
json_method_id = args[u"id"]
result = json_func(json_params)
args.pop(u"method")
args["result"] = result[0]
args["error"] = None
return Response(simplejson.dumps(args), content_type="application/json")
else:
return Response('Error')



That's all. Please enjoy :-)

Kay で pyjamas を使う

日本語の情報もできたのでちょっと変わり種をやります。

Python 版 GWT である pyjamas を Kay と組み合わせてみました.. と言っても jsonrpc の簡単なサンプルを動かしただけですが。

Pyjamas をインストールした場所を ${PYJAMAS_HOME} と、プロジェクトのディレクトリを ${PROJECT_DIR} として進めましょう。

${PYJAMAS_HOME}/examples/jsonrpc をいじって使います。public 以下は JSONRPCExample.html だけ必要みたい。


$ tree kay-pyjamas-sample
kay-pyjamas-sample
|-- JSONRPCExample.py
`-- public
`-- JSONRPCExample.html

1 directory, 2 files


JSONRPCExample.py を下記のように少しいじります。

import pyjd # dummy in pyjs

from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.TextArea import TextArea
from pyjamas.ui.Label import Label
from pyjamas.ui.Button import Button
from pyjamas.ui.HTML import HTML
from pyjamas.ui.VerticalPanel import VerticalPanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.ListBox import ListBox
from pyjamas.JSONService import JSONProxy

class JSONRPCExample:
def onModuleLoad(self):
self.TEXT_WAITING = "Waiting for response..."
self.TEXT_ERROR = "Server Error"
self.METHOD_ECHO = "Echo"
self.METHOD_REVERSE = "Reverse"
self.METHOD_UPPERCASE = "UPPERCASE"
self.METHOD_LOWERCASE = "lowercase"
self.methods = [self.METHOD_ECHO, self.METHOD_REVERSE, self.METHOD_UPPERCASE, self.METHOD_LOWERCASE]

self.remote_py = EchoServicePython()

self.status=Label()
self.text_area = TextArea()
self.text_area.setText("""{'Test'} [\"String\"]
\tTest Tab
Test Newline\n
after newline
""" + r"""Literal String:
{'Test'} [\"String\"]
""")
self.text_area.setCharacterWidth(80)
self.text_area.setVisibleLines(8)

self.method_list = ListBox()
self.method_list.setName("hello")
self.method_list.setVisibleItemCount(1)
for method in self.methods:
self.method_list.addItem(method)
self.method_list.setSelectedIndex(0)

method_panel = HorizontalPanel()
method_panel.add(HTML("Remote string method to call: "))
method_panel.add(self.method_list)
method_panel.setSpacing(8)

self.button_py = Button("Send to Python Service", self)

buttons = HorizontalPanel()
buttons.add(self.button_py)
buttons.setSpacing(8)

info = """

JSON-RPC Example


This example demonstrates the calling of server services with
JSON-RPC.


Enter some text below, and press a button to send the text
to an Echo service on your server. An echo service simply sends the exact same text back that it receives.

"""

panel = VerticalPanel()
panel.add(HTML(info))
panel.add(self.text_area)
panel.add(method_panel)
panel.add(buttons)
panel.add(self.status)

RootPanel().add(panel)

def onClick(self, sender):
self.status.setText(self.TEXT_WAITING)
method = self.methods[self.method_list.getSelectedIndex()]
text = self.text_area.getText()
id = -1
if method == self.METHOD_ECHO:
id = self.remote_py.echo(text, self)
elif method == self.METHOD_REVERSE:
id = self.remote_py.reverse(text, self)
elif method == self.METHOD_UPPERCASE:
id = self.remote_py.uppercase(text, self)
elif method == self.METHOD_LOWERCASE:
id = self.remote_py.lowercase(text, self)
if id<0:
self.status.setText(self.TEXT_ERROR)

def onRemoteResponse(self, response, request_info):
self.status.setText(response)

def onRemoteError(self, code, message, request_info):
self.status.setText("Server Error or Invalid Response: ERROR %d - %s" %
(code, message))


class EchoServicePython(JSONProxy):
def __init__(self):
JSONProxy.__init__(self, "/json", ["echo", "reverse", "uppercase", "lowercase"])

if __name__ == '__main__':
# for pyjd, set up a web server and load the HTML from there:
# this convinces the browser engine that the AJAX will be loaded
# from the same URI base as the URL, it's all a bit messy...
pyjd.setup("http://127.0.0.1/examples/jsonrpc/public/JSONRPCExample.html")
app = JSONRPCExample()
app.onModuleLoad()
pyjd.run()


そしたらそのディレクトリに入ってコンパイルします。今回はターゲットを直
接 ${PROJECT_DIR}/media にしてしまいます。


$ cd kay-pyjamas-sample
$ ${PYJAMAS_HOME}/bin/pyjsbuild -o ${PROJECT_DIR}/media JSONRPCExample.py


成功すると ${PROJECT_DIR}/media/JSONRPCExample.html が出来ます。
http://localhost:8080/media/JSONRPCExample.html で画面が出ていればまあ成功です。

サーバーサイドは下記の通りにします。myapp はアプリケーション名に読み換
えてくださいね。

${PROJECT_DIR}/myapp/urls.py

# -*- coding: utf-8 -*-
# myapp.urls


from werkzeug.routing import (
Map, Rule, Submount,
EndpointPrefix, RuleTemplate,
)
import myapp.views

def make_rules():
return [
EndpointPrefix('myapp/', [
Rule('/', endpoint='index'),
Rule('/json', endpoint='json_rpc'),
]),
]

all_views = {
'myapp/index': myapp.views.index,
'myapp/json_rpc': myapp.views.json_rpc,
}


最後に view です。

${PROJECT_DIR}/myapp/views.py

# -*- coding: utf-8 -*-
# myapp.views
# ... 省略

import simplejson

# ... 省略

def json_uppercase(args):
return [args[0].upper()]

def json_echo(args):
return [args[0]]

def json_reverse(args):
return [args[0][::-1]]

def json_lowercase(args):
return [args[0].lower()]

def json_rpc(request):
if request.method:
args = simplejson.loads(request.data)
method_name = 'json_%s' % args[u"method"]
json_func = globals().get(method_name)
json_params = args[u"params"]
json_method_id = args[u"id"]
result = json_func(json_params)
args.pop(u"method")
args["result"] = result[0]
args["error"] = None
return Response(simplejson.dumps(args), content_type="application/json")
else:
return Response('Error')



適当な作りですが、まあこれで動きます。興味があれば色々工夫してみてくだ
さい。

今日はここでおしまいです。

August 4, 2009

Kay framework を使ってみる

Kay framework を 7/7 にリリースしたわけですが、日本語での情報が無いので少しずつ書いていくことにしようと思います。

始め方


必要なものは下記の通りです。

  • Python-2.5
  • App Engine SDK Python
  • Kay framework


macports を使って python25 を入れた場合は、他に下記もインストールしましょう。

  • py25-hashlib
  • py25-socket-ssl
  • py25-pil
  • py25-ipython


Kay のリリース版を使う場合は、下記のダウンロードページから tar ball を落として使います。リポジトリを追いかける場合は、mercurial で clone してください。

ダウンロードページ: http://code.google.com/p/kay-framework/downloads/list

clone するには:

$ hg clone https://kay-framework.googlecode.com/hg/ kay


プロジェクトを始める


kay の manage.py スクリプトでプロジェクトディレクトリを作る事ができます。

$ python kay/manage.py startproject myproject
$ tree myproject
myproject
|-- app.yaml
|-- kay -> /Users/tmatsuo/work/tmp/kay/kay
|-- manage.py -> /Users/tmatsuo/work/tmp/kay/manage.py
|-- settings.py
`-- urls.py

1 directory, 4 files


シンボリックリンクをサポートしているプラットフォームでは kay ディレクトリと manage.py へのシンボリックリンクが作成されます。後で kay の場所を動かすときっと動かなくなるのですが、そんな時はリンクを張り直してください。

アプリケーションを作る


出来たばかりの myproject ディレクトリに cd して、早速アプリケーションを作りましょう。

$ cd myproject
$ python manage.py startapp myapp
$ tree myapp
myapp
|-- __init__.py
|-- models.py
|-- templates
| `-- index.html
|-- urls.py
`-- views.py

1 directory, 5 files


こうして作成した myapp を動作させるにはもうひと手間必要です。settings.py の INSTALLED_APPS に登録します。必要なら APP_MOUNT_POINTS も登録します。下記の例では、アプリケーションをルート URL にマウントする例です。APP_MOUNT_POINTS を設定しない場合は、/myapp というようにアプリケーション名 URL にマウントされます。

settings.py

#$/usr/bin/python
#..
#..

INSTALLED_APPS = (
'kay.sessions',
'myapp',
)

APP_MOUNT_POINTS = {
'myapp': '/',
}


見れば分かると思いますが、INSTALLED_APPS はタプルで、APP_MOUNT_POINTS は dict になっています。

アプリケーションを動かす


作ったアプリケーションを動かしてみましょう。下記のコマンドで開発サーバが起動する筈です。

$ python manage.py runserver
INFO 2009-08-04 05:48:21,339 appengine_rpc.py:157] Server: appengine.google.com
...
...
INFO 2009-08-04 05:48:21,448 dev_appserver_main.py:465] Running application myproject on port 8080: http://localhost:8080

この状態で http://localhost:8080/ にアクセスすると、「Hello」又は「こんにちは」と表示される筈です。

GAE にアップロードする


GAE にアップロードするには、対象の appid を app.yaml の application に設定してから、下記のコマンドを使用します。

$ python manage.py appcfg update


成功すると、http://your-appid.appspot.com/ でアクセスできるようになります。

今日はここまでにしますね。けっこう簡単に始められる事がお分かりいただけたでしょうか。次回はモデル定義とか、フォームの自動生成などについてお話しする予定です。

当分このシリーズを続けるつもりなので、取り上げて欲しいトピックなどがあればコメントください。