Hatena::ブログ(Diary)

Rails etc... Memo

2009-11-03

[]Macの場合

Mac OS Xにて、GAEの新規プロジェクトを作る方法は、Windowsと少し違っていたのでメモ。(Windowsでの自分のやり方が違うのかもだけど)

  1. GoogleAppEngineLauncher.app を起動する
  2. Fileメニューから New Application を選択
  3. 任意のアプリケーション名を入力
  4. ApplicationDirectoryを選択(デフォルト値のままでよい)

でおわり。

ApplicationDirectoryの配下に、アプリケーション名でフォルダが作られ、その中に必要最小限のファイルが作成される。

  • app.paml
  • index.yaml
  • main.py

実行させるときは、「Run」ボタン押すだけ。

この方法で最低限のプロジェクトを作って、Aptana側でこのフォルダをプロジェクトとして開き、そこにソース追加したりしていけばいいようだ。

Windows上でAptana使ってプロジェクト作成するときは、PYTHONPATHがどうとか、GAEのパスがどうとか、気にしないといけなかったのだが、全く気にせずにできてしまった。簡単。


追記(2009/11/04)

自分の環境は、MacOS X Snow Leopard なのであるが、元々、Python2.3, 2.5, 2.6 がインストールされており、CurrentVersionは2.6となっている。このままだと、GoogleAppEngineLauncher.appも、Python2.6にて起動してしまうので、次の手順にて2.5を使うように設定する必要がある。

  • GoogleAppEngineLauncher.appを起動し
  • Preferencesを開き
  • Python Path に、「/usr/bin/python2.5」と入力する。

追記(2009/12/30)

このやり方だと、Aptana上で、GAEがらみのモジュールがコード・コンプリートされない(入力候補が出てこない)。

コード・コンプリートさせたい場合は次の手順で。(これをやりたくて、これを調べていたのだ。)

  • プロジェクトで右クリック→プロパティ→Pydev-PYTHONPATH のページを開く。
  • String Substitution Variables に、次の変数を追加
    • キー:GOOGLE_APP_ENGINE
    • 値 :/usr/local/google_appengine
  • External Libraries にて、「Add based on variable」から、次のものを追加
    • ${GOOGLE_APP_ENGINE}
    • ${GOOGLE_APP_ENGINE}/lib/django
    • ${GOOGLE_APP_ENGINE}/lib/webob
    • ${GOOGLE_APP_ENGINE}/lib/yaml

[]外部モジュールが読み込めない件(その4)少し理解してきたかも

今日から本格的に自宅のMacで作業しはじめたのだが、上記の手順で作ったプロジェクトのmain.pyに、軽い気持ちでimport BeautifulSoup と書いて実行させたら、またぞろエラーになったので、GAEのドキュメントを読んでいたら、その件についてちゃんと書いてあった。

http://code.google.com/intl/ja/appengine/docs/python/runtime.html#Pure_Pythonより引用:

Python モジュールのインクルード パスにはアプリケーションのルート ディレクトリ(app.yaml ファイルを含むディレクトリ)が含まれます。アプリケーションのルート ディレクトリに作成するモジュールはルートからのパスを使用することで使用可能になります。Python がサブ ディレクトリをパッケージと認識できるように、忘れずに __init__.py ファイルをサブ ディレクトリに作成してください。

ということは、こういうことか。

  • プロジェクトのルートディレクトリ配下に、たとえば「lib」という名前のフォルダを作成する。
  • そこに、BeautifulSoup.py とかを置く。
  • そこに、__init__.py というファイルを作って置く。中身はからっぽでOK

ここまでやったら、ソースコード上にこう書く。

import lib.BeautifulSoup
または
from lib import BeautifulSoup

んで実行したら・・・動いた。

参考:http://atkonn.blogspot.com/2008/02/python-python38.html

ドキュメント読んでて思ったのだが、コンソール(コマンドプロンプト)で対話形式のpython動かして、そこで import BeautifulSoup ってやってもエラーにならないのは、PYTHONPATHによってサーチパスが切られている場所(site-packagesとか)にBeautifulSoup.pyが置かれているからだけど、GAE上で動いているPythonアプリケーションは、そこを見に行く訳じゃないっていうことなんだろうか。

ドキュメントによれば、GAEPython環境には、ピュアPythonの他に、サードパーティライブラリとして、Django, WebOb, PyYAMLが含まれている、と書いてあるが、逆に言うと、それ以外のサードパーティーライブラリは含まれていない(含ませることはできない)ということだから、本番環境の.../Python/lib/site-packagesにBeautifulSoup.pyを置く、みたいなことはできないわけで、そうである以上、アプリケーションディレクトリ配下に置いて、それをパッケージとして認識させなきゃいけない、ということなのだろう。

今まで、出来ない出来ないと言っていたのは、すべて、GAE上で動かすアプリケーションだった。普通のPythonプロジェクトであれば問題はなかったのだから、python と、google app engine がごちゃごちゃになっていたためにハマってしまったということだ。

2009/11/10 追記

上記と同じ事を、Windows環境でもやってみたのだが・・・importできなかった。

なんだかもうよくわからない。

Windows環境では、GoogleAppEngineLauncherを使っていないなどの違いもあり、原因が絞り込めないので、その3で試した方法に戻すことにした。

2009/12/02 追記

その後、いろいろ試すうちに、問題なくImportできる方法が確立したと思えるので、こちらにまとめた。

結論をひとことで言うと「GoogleAppEngineLauncherを使って新規プロジェクトを追加するほうがいい」。

2014/9/24 追記

なんと5年越しにこの問題に決着がついた。やっと理解。上にいろいろ書いてきたことは結構的外れだった。

http://d.hatena.ne.jp/noazoh/20140924/1411553513

[]画面ごとにpyファイルを分けるときの注意点

GAEのドキュメントの「スタートガイド」に出てくる、おなじみのミニマムコード。

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class MainPage(webapp.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    self.response.out.write('Hello, webapp World!')

application = webapp.WSGIApplication(
                                     [('/', MainPage)],
                                     debug=True)
def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

一方こちらは、GoogleAppEngineLauncher や、AptanaからGAEの新規プロジェクトを作る際の雛形として使われるミニマムコード。(これは、GAEインストール先の配下、new_project_templateというフォルダの下にmain.pyという名前で存在する)

import wsgiref.handlers
from google.appengine.ext import webapp

class MainHandler(webapp.RequestHandler):

  def get(self):
    self.response.out.write('Hello world!')

def main():
  application = webapp.WSGIApplication([('/', MainHandler)],
                                       debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
  main()

似てるようで少し違うこの2つのコードであるが、前者に肉付けしていったほうが、後々幸せになれる。

この事は、2つ画面を作って、メイン画面からサブ画面に遷移する、という処理を試していたときに気づいた。自分は、後者の雛形に肉付けする形でコードを書いていた。

スタートガイドを参考にして、class MainForm と class SubForm を定義して、MainForm を起動して、submit ボタン押したら SubForm へ遷移させる、という動作をさせることには成功した。ただ、実際の開発現場においては、1本のソースファイルにすべての処理を詰め込むなんてことはしないので、試しに class SubForm を別のpyファイルに外出ししてみたのだが、遷移できなくなってしまった。理由は、getやpostをハンドリングする application というオブジェクトが、def main() 内のローカルスコープとなっているために、main()を抜けると、ハンドリングする人がいなくなってしまうのが原因であった。前者のように、applicationオブジェクトの宣言を、main()の外に書いたら遷移できるようになった。

テンプレートを用意してくれるのはいいが、中身は前者にしてくれればいいのに・・・

当初、前者と後者では何が違うんだろうと思いつつも、main()の前に処理があるのに違和感を感じて、後者の雛形を選んだのだが、そのおかげで意味がわかってよかったのだが。