2008/04/28
床屋
髪切り落とされる様をただ見る時間。髭をそるクリームを塗られ、髭を剃られるまでの「じらし」。これも床屋の魅力であると思う。
別にしっかり肩を「パンパン」叩いてくれとも思わない。しっかり叩いたら床屋で無くなる気がする。
もしかしたら、コンピュータ業界も同じなのかもしれない。
要件定義から、すぐさま答えの出る業界ならば面白くもなんともないかもしれない。
ああでもない。こうでもないと考えてこそ面白いのであって、仕様が、そして納期が決められているからこそ面白いのだ。別の担当者が作る部品の完成をドキドキしながら待つ時間。それも床屋と同じと考えれば気が楽になったりしないだろうか。しないか...
もしかしたら気の効きすぎた、仕事を100%こなすソフトウェアというのは、実は魅力的でないのかもしれない。
プログラミング言語を取ってみても、各言語毎にそれぞれライブラリがあり目的を達成させる為の近道になる物が沢山ある。
それも良い事だとは思うけれど、気の効いてない言語もまた楽しかったりするんだろうな。
でも、髭くらいはちゃんと剃れ。
2008/04/24
Google App EngineでXMLをパースするときに手っ取り早い方法
*** elementtree/ElementTree.py.orig Thu Apr 24 21:02:09 2008ちなみにpatch当てなくても、ElementTree.pyの最下行に
--- elementtree/ElementTree.py Thu Apr 24 21:34:42 2008
***************
*** 1252,1254 ****
--- 1252,1267 ----
tree = self._target.close()
del self._target, self._parser # get rid of circular references
return tree
+
+ try:
+ from xml.parsers import expat
+ except ImportError:
+ import xmllib
+ import SimpleXMLTreeBuilder
+ def __init__fake(self, html=0):
+ self.__init__orig(html)
+ xmllib.XMLParser.__init__(self, accept_utf8=1)
+ clazz = SimpleXMLTreeBuilder.TreeBuilder
+ clazz.__init__orig = clazz.__init__
+ clazz.__init__ = __init__fake
+ XMLTreeBuilder = SimpleXMLTreeBuilder.TreeBuilder
を足せば出来上がる。こうすると大概のものは小細工無しに動くと思う。たぶん
try:
from xml.parsers import expat
except ImportError:
import xmllib
import SimpleXMLTreeBuilder
def __init__fake(self, html=0):
self.__init__orig(html)
xmllib.XMLParser.__init__(self, accept_utf8=1)
clazz = SimpleXMLTreeBuilder.TreeBuilder
clazz.__init__orig = clazz.__init__
clazz.__init__ = __init__fake
XMLTreeBuilder = SimpleXMLTreeBuilder.TreeBuilder
エラーが出てたGoogle App Engineのサンプル、muvmuvのレビュービルド「http://localhost:8080/build」も問題なく通った。
良いやり方ではないですが...
Tagged as: google app engine, python
Bookmarks:
Qaaに回答を投げるMinibufferコマンド書いた
Qaa(クァー):Twitterで簡単アンケート!(まだまだ開発中)twitterを使ったソーシャルアンケートサービスですね。
![]()
Qaa(クァー)は、Twitterを利用して簡単にアンケートをとることができるサービスです。
Qaa(クァー)を通じて投稿されたアンケートはYes or No形式で回答する事ができ、その結果はアンケート毎に用意されるページでパイチャートで確認する事ができます。
http://qaa.orig.jp/h#about
でもいちいちtwitterやIMから「@qaa qid14 YES」とか入力するの面倒臭いのでMinibufferコマンド作ってみました。
アンケートエントリページに行って、「q y」もしくは「q n」で「YES」もしくは「NO」の回答をtwitter経由で投稿します。
qaa_yes_or_no.user.jsよろしければどうぞ。
※一応、firefox3/greasemonkeyからBasic認証のダイアログが出ない件は対応してあります。
Tagged as: greasemonkey, minibuffer, qaa
Bookmarks:
pytumblrをGoogle App Engineで動くように改造してみた
freshmeat.net: Project details for pytumblr - ロックスターになりたい
pytumblr is a Python library for the tumblr.com API. freshmeat.net: Project details for pytumblr
そう言えば前に「Windowsのエクスプローラで「送る」からShareOnTumblr」なんてのも作ったなぁ。
pythonで作ってあってlinuxなんかでも動くように作ったはず。でpytumblrですが、ソース見たら簡単なソースだったのでGoogle App Engineで動くように改造してみました。以下パッチ
--- pytumblr.py.orig Thu Apr 24 03:15:26 2008思いっきり弄ってますね。。。
+++ pytumblr.py Thu Apr 24 12:02:32 2008
@@ -1,7 +1,9 @@
#!/usr/bin/env python
+#!-*- coding:utf-8 -*-
-import string, httplib, urllib2, urllib
-from xml.dom import minidom
+import urllib2, urllib
+from google.appengine.api import urlfetch
+from BeautifulSoup import BeautifulSoup
class pytumblr(object):
"""Tumblr API Object.
@@ -75,14 +77,11 @@
data = urllib.urlencode(values)
headers = {"Content-type": "application/x-www-form-urlencoded"}
- conn = httplib.HTTPConnection(self.url)
- conn.follow_all_redirects = True
- conn.request("POST",'/api/write', data, headers)
- response = conn.getresponse()
- if ( int(response.status) == 201):
+ response = urlfetch.fetch("http://%s/api/write" % self.url, headers=headers, method='POST', payload=data)
+ if ( int(response.status_code) == 201):
return 'Success'
- elif( int(response.status) != 201):
- raise 'Error - Status %s (%s) returned' %(response.status, response.reason)
+ elif( int(response.status_code) != 201):
+ raise 'Error - Status %s (%s) returned' %(response.status_code, 'something wrong')
def auth(self):
values = {
@@ -92,13 +91,10 @@
}
data = urllib.urlencode(values)
headers = {"Content-type": "application/x-www-form-urlencoded"}
- conn = httplib.HTTPConnection(self.url)
- conn.follow_all_redirects = True
- conn.request("POST", '/api/write', data, headers)
- response = conn.getresponse()
- if ( int(response.status) != 200 ):
- return '< There was a tiny problem: %s (%s) >' %(response.status, response.reason)
- if ( int(response.status) == 200 ):
+ response = urlfetch.fetch("http://%s/api/write" % self.url, headers=headers, method='POST', payload=data)
+ if ( int(response.status_code) != 200 ):
+ return '< There was a tiny problem: %s (%s) >' %(response.status_code, 'something wrong')
+ if ( int(response.status_code) == 200 ):
return '< Authenticated! >'
@@ -112,25 +108,8 @@
opts = opts + "?type=" + type
if ( id != 'None' ):
opts = opts + "?id=" + id
- return urllib.urlopen('http://%s.tumblr.com/api/read%s' %(self.user, opts)).read()
+ return urlfetch.fetch('http://%s.tumblr.com/api/read%s' %(self.user, opts)).content
def getblog(self):
- rxml = minidom.parseString(self.blogread())
- titles = rxml.getElementsByTagName('regular-title')
- postid = rxml.getElementsByTagName('post')
- i = 0
- n = 0
- posts = {}
- while ( i < len(postid)):
- if ( postid[i].attributes['type'].value == 'regular' ):
- t = titles [n]
- t = t.toxml()
- t = t.replace('<regular-title)', '')
- t = t.replace('</regular-title)', '')
- poid = postid[i].attributes["id"].value
- posts[ poid ] = t
- n = n + 1
- if ( postid[i].attributes['type'].value != 'regular' ):
- pass
- i = i + 1
- return posts
+ soap = BeautifulSoup(self.blogread())
+ return soap('post')
minidomの代わりにBeautifulSoupを、urllib2の代わりにurlfetchを使っています。したがってgetblogは自分のregular情報のdictだけを返すのではなくBeautifulSoupを使ってlink、photo、quoteを返す様にしてあります。
実際に動くよって所は以下のサイトで確認して下さい。
pytumblrこのサイトのURLの後ろに
http://mattn.appspot.com/tumblr/mattnと言った感じにtumblrアカウント名を付けてみて下さい。
今回のデモにはweb.pyというフレームワークを使用してみました。
以下スクリプトソースです。
#!-*- coding:utf-8 -*-案外短く書けますね。そしてテンプレートHTML
import web
from pytumblr import pytumblr
from BeautifulSoup import BeautifulSoup
def unescape(str):
return BeautifulSoup(str, convertEntities=BeautifulSoup.HTML_ENTITIES).contents[0].encode('utf-8', 'replace')
urls = (
'/tumblr/(.*)', 'tumblr'
)
render = web.template.render('templates/')
class tumblr:
def GET(self, name):
web.header("Content-Type", "text/html; charset=utf-8")
template_values = { 'name': '', 'posts': [] }
name = name.replace('/', '')
if name:
pt = pytumblr(name, None, None)
template_values['name'] = name
for blog in pt.getblog():
if blog['type'] == 'link':
template_values['posts'].append({
'type': 'link',
'text': unescape(blog('link-text')[0].string),
'desc': blog('link-description') and unescape(blog('link-description')[0].string) or '',
'link': unescape(blog('link-url')[0].string),
})
if blog['type'] == 'photo':
template_values['posts'].append({
'type': 'photo',
'text': unescape(blog('photo-caption')[0].string),
'link': unescape(blog('photo-url')[0].string),
})
if blog['type'] == 'quote':
template_values['posts'].append({
'type': 'quote',
'text': unescape(blog('quote-text')[0].string),
'link': unescape(blog('quote-source')[0].string),
})
return render.tumblr(template_values)
if __name__ == "__main__":
web.application(urls, globals()).cgirun()
$def with (res)このweb.pyのテンプレートって癖があってpythonインデント方式なのですが、これってpreとかcodeで先頭が入っちゃったら不味いんじゃないかと思ったり...
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="/static/css/tumblr.css" type="text/css" />
$if res['name']:
<title>pytumblr - $res['name']</title>
$else:
<title>pytumblr</title>
</head>
<body>
$if res['name']:
<h1>pytumblr - $res['name']</h1>
$else:
<h1>pytumblr</h1>
<img src="http://b.hatena.ne.jp/entry/image/http://mattn.appspot.com/tumblr/" title="はてなブックマーク" />
<div id="content">
$if res['name']:
<h2>$res['name']'s tumblr</h2>
$for post in res['posts']:
$if post['type'] == 'photo':
<b>PHOTO:</b>$:post['text']<br />
<blockquote><img src="$post['link']" /></blockquote>
$elif post['type'] == 'link':
<b>LINK:</b><a href="$post['link']">$:post['text']</a><br />
<blockquote class="link">$:post['desc']</blockquote>
$elif post['type'] == 'quote':
<b>QUOTE:</b><br />
<blockquote class="quote">
$:post['text']<br />
<cite>$:post['link']</cite>
</blockquote>
</div>
<hr clear="all" />
<p style="text-align: center">provided by <a href="http://mattn.kaoriya.net">mattn</a>, hosted on google app server.</p>
</body>
</html>
解決方あるのか、調べてみます。pytumblrで面白いもの作ってみて下さい。
Tagged as: google app engine, python, pytumblr, tumblr
Bookmarks:
2008/04/23
Google App Engineでweb.pyやCherryPyを動かす方法
Google App Engineでは、以下で述べられているように「WSGIに対応したCGI(と呼ばれるもの)であれば、フレームワークとして扱えるよ」と言っています。
Using the webapp Framework - Google App Engine - Google Code今日はこの動かないと言われている部分を解決して頂ける(かもしれない)ポイントをご紹介。
The CGI standard is simple, but it would be cumbersome to write all of the code that uses it by hand. Web application frameworks handle these details for you, so you can focus your development efforts on your application's features. Google App Engine supports any framework written in pure Python that speaks CGI (and any WSGI-compliant framework using a CGI adaptor), including Django, CherryPy, Pylons, and web.py. You can bundle a framework of your choosing with your application code by copying its code into your application directory.
http://code.google.com/appengine/docs/gettingstarted/usingwebapp.html
web.py
まずweb.pyですが、オフィシャルがtarballで配布している物(web.py-0.23.tar.gz)では動きません。最新はhttp://webpy.org/bzr/webpy.dev/で配布されており、bzrを使って取得する必要があります(必要であればhttp://bazaar-vcs.org/Downloadからbzrを取得して下さい)。bzr get http://webpy.org/bzr/webpy.dev/これまでの1スクリプトからソケットサーバまで起動するweb.pyのコーディング習慣ではそのままでは動きません。flupを使ってWSGIモジュールを動かす必要があります。以下の様にWSGIハンドラとしてアプリケーションを作成しwsgiref.handlersに起動させます。mod_pythonの場合と同じですかね。
ソースコードの中を覗いた所、Google App Engineに対応するコードが入っています。以前までは一般的なCGIとして「print "Hello World!"」してしまえばそのまま出力されていましたが方式が変った様で、GET等はreturnで文字列として返す様になっています。
動くコードとしては以下の様になります。
#!-*- coding:utf-8 -*-但し、「web.pyが内部でimportしているopenid.consumerが無いよ!」と怒られるのでpython-openidをいっそ入れてしまうか、以下のパッチを当てる必要があります。
import web
urls = (
'/hello/(.*)', 'hello'
)
class hello:
def GET(self, name):
web.header("Content-Type", "text/html; charset=utf-8")
return "Hello World!"
if __name__ == "__main__":
web.application(urls, globals()).cgirun()
--- web/__init__.py.orig Wed Apr 23 19:12:33 2008まだ開発版の様ですから、今後に期待したいです。
+++ web/__init__.py Wed Apr 23 19:12:35 2008
@@ -26,7 +26,7 @@
from httpserver import *
from debugerror import *
from application import *
-import webopenid as openid
+#import webopenid as openid
try:
import cheetah
CherryPy
次にCherryPyですが少し小細工が必要です。Google App EngineではPure Pythonで無いものは動かないのですがCherryPyに含まれるWSGIServer(実際にはSSL機能)がsocket._fileobjectを使ってしまっていてモジュールのインポートに失敗します。以下の様にしてSSL_fileobjectを殺してやる必要があります。--- cherrypy/wsgiserver/__init__.py.orig Sun Jan 13 17:56:50 2008あとはweb.py同様に
+++ cherrypy/wsgiserver/__init__.py Wed Apr 23 16:47:34 2008
@@ -57,11 +57,12 @@
from urllib import unquote
from urlparse import urlparse
-try:
- from OpenSSL import SSL
- from OpenSSL import crypto
-except ImportError:
- SSL = None
+#try:
+# from OpenSSL import SSL
+# from OpenSSL import crypto
+#except ImportError:
+# SSL = None
+SSL = None
import errno
socket_errors_to_ignore = []
@@ -676,19 +677,19 @@
raise socket.timeout("timed out")
return ssl_method_wrapper
-class SSL_fileobject(socket._fileobject):
- """Faux file object attached to a socket object."""
-
- ssl_timeout = 3
- ssl_retry = .01
-
- close = _ssl_wrap_method(socket._fileobject.close)
- flush = _ssl_wrap_method(socket._fileobject.flush)
- write = _ssl_wrap_method(socket._fileobject.write)
- writelines = _ssl_wrap_method(socket._fileobject.writelines)
- read = _ssl_wrap_method(socket._fileobject.read, is_reader=True)
- readline = _ssl_wrap_method(socket._fileobject.readline, is_reader=True)
- readlines = _ssl_wrap_method(socket._fileobject.readlines, is_reader=True)
+#class SSL_fileobject(socket._fileobject):
+# """Faux file object attached to a socket object."""
+#
+# ssl_timeout = 3
+# ssl_retry = .01
+#
+# close = _ssl_wrap_method(socket._fileobject.close)
+# flush = _ssl_wrap_method(socket._fileobject.flush)
+# write = _ssl_wrap_method(socket._fileobject.write)
+# writelines = _ssl_wrap_method(socket._fileobject.writelines)
+# read = _ssl_wrap_method(socket._fileobject.read, is_reader=True)
+# readline = _ssl_wrap_method(socket._fileobject.readline, is_reader=True)
+# readlines = _ssl_wrap_method(socket._fileobject.readlines, is_reader=True)
class HTTPConnection(object):
import cherrypyとすればGoogle App Engine上でも動きます。上の例にあるCherryPy独特の「/helloworld/onepage」も動きますよ!
import wsgiref.handlers
class OnePage(object):
def index(self):
return "one page!"
index.exposed = True
class HelloWorld(object):
onepage = OnePage()
def index(self):
return "hello world"
index.exposed = True
def main():
app = cherrypy.Application(HelloWorld(), "/helloworld")
wsgiref.handlers.CGIHandler().run(app)
if __name__ == '__main__':
main()
これで幾らかのフレームワークが動くようになりました。幾らか敷居が低くなるのではないでしょうか。皆さんも色んなアプリケーションを作ってみませんか。
また時間が出来たら、残るPylonsも検証して見たいと思います。
最後に私が好きなCDを...
Tagged as: cherrypy, google app engine, python, web.py
Bookmarks:
2008/04/22
twitter followerの小難しい発言に読みをふってくれるサービス「よみふったー」をリリースした。
twitterのfollower発言で、漢字が読めなかった貴方。「@mattn_jp それなんて読むの?」とか聞くのが恥ずかしい貴方。そんな貴方にピッタリのサービスです。twitter followerの発言に「読み」を付けて表示します。(まぁ私は難しい単語なんて使いませんが...)
文字の分解にはYahoo! JAPANの「日本語形態素解析Webサービス」を使用しています。
色の変っている部分にマウスを当てると、ツールチップにて読みを教えてくれるようになっています。
で、いきなりですが「よみふったー」のソースです。![]()
ライブラリとしてはwebSimple.pyのほかにPyWrapperに含まれるElementTree、BeautifulSoupを使っています。
#!-*- coding:utf-8 -*-適当なコードで申し訳ない...汗
import os
import re
import base64
import xmllib
import logging
import elementtree.SimpleXMLTreeBuilder as xmlbuilder
import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from BeautifulSoup import BeautifulSoup
from webSimple import Simple as webSimple
class MainPage(webapp.RequestHandler):
def post(self):
twitter_user = self.request.get('twitter_user').encode('utf-8', 'replace')
template_valuse = {
'twitter_user' : twitter_user,
'statuses' : []
}
try:
twitter_url = "http://twitter.com/statuses/user_timeline/%s.xml" % twitter_user
twitter = webSimple({ 'base_url': twitter_url })
yahoo = webSimple({
'base_url': 'http://api.jlp.yahoo.co.jp/MAService/V1/parse',
'param' : { 'appid' : 'xxxxxxxx', 'results' : 'ma', },
})
xml = twitter.get().content
r = re.compile(r'(\&#\d+;)')
for st in BeautifulSoup(xml)('status'):
name = r.sub(lambda x: unichr(int(x.group(1)[2:-1])), st.user.screen_name.string).encode('utf-8', 'replace')
msg = r.sub(lambda x : unichr(int(x.group(1)[2:-1])), st.text.string).encode('utf-8', 'replace')
xml = yahoo.get({ 'sentence': msg }).content
words = []
for word in BeautifulSoup(xml)('word'):
words.append({
'reading' : word.reading.string.encode('utf-8', 'replace'),
'pos' : word.pos.string.encode('utf-8', 'replace'),
'surface' : word.surface.string.encode('utf-8', 'replace'),
})
template_valuse['statuses'].append({
'screen_name' : name,
'words' : words,
})
except Exception, e:
template_valuse['error'] = e
pass
path = os.path.join(os.path.dirname(__file__), 'yomifutter.html')
self.response.out.write(template.render(path, template_valuse))
def get(self):
path = os.path.join(os.path.dirname(__file__), 'yomifutter.html')
self.response.out.write(template.render(path, {}))
def main():
application = webapp.WSGIApplication([('/yomifutter/', MainPage)], debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()
Tagged as: google app engine, python, twitter, yomifutter
Bookmarks:
2008/04/21
wedataのデータを扱えるpythonモジュール書いた
WedataオブジェクトのコンストラクタにAPIKEYを掘り込んで操作します。
api = Wedata('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')また、AutoPagerizeのデータベースであれば以下の様にdataプロパティからの属性参照の様に書く事も出来ます。
databases = api.databases()
for database in databases:
print database
for key in database.keys():
print " %s=%s" % (key, database[key])
database = api.database('AutoPagerize')Database.create_databaseの戻り値にはデータベース名(キー名)、Item.create_itemの戻り値にはアイテムIDが戻ります。このIDを使ってDatabase.delete_datebaseおよびItem.dalete_itemを呼び出す事が出来ます。
print "%s : %s" % (database.name, database.description)
items = database.items()
for item in database.items():
print " %s" % item.data.pageElement
print " %s" % item.data.insertBefore
dbid = api.create_database('my_example_database', 'my_example_database', ['name', 'description'], ['value', 'xpath'], False)今のところGoogle App Engineには対応していません。Google App Engine上での用途を見付けたら対応するかもしれません。
api.delete_database(dbid)
いつものようにコードはcodereposに置いてあります。
/lang/python/wedata
Tagged as: python, wedata
Bookmarks:
2008/04/18
Google App Engineで、fk_2000氏と愉快な仲間達がお届けする「世界のナベアツ」を作った
世界のナベアツにはてなブックマークで挑戦する - ネットランダムなんと!ソフトウェアでやるべきネタを人力で!!!
はてなブックマークのコメントで、1から40まで数えて、3の倍数 と3がつく数字の時だけアホになります。
それではどうぞ!
http://d.hatena.ne.jp/fk_2000/20080418/p2
むむむ...こうなればこれをまたソフトウェアで表現するべき!
fk_2000氏と愉快な仲間達がお届けする「世界のナベアツ」
ちなみにコードはこんなの
#!-*- coding:utf-8 -*-
import os
from google.appengine.ext.webapp import template
from google.appengine.api import urlfetch
from BeautifulSoup import BeautifulSoup
import logging
try:
res = urlfetch.fetch('http://b.hatena.ne.jp/entry/rss/http://d.hatena.ne.jp/fk_2000/20080418/p2')
print "Content-Type: text/html; charset=UTF-8"
print ""
items = []
for item in BeautifulSoup(res.content)('item'):
logging.info(item.title)
logging.info(item.title.string)
items.insert(0, {
'title' : item.title.string,
'description' : item.description.string,
})
template_values = {
'items': items
}
path = os.path.join(os.path.dirname(__file__), 'nabeatzz_fk_2000.html')
print template.render(path, template_values)
except Exception, e:
print "Status: 400"
print ""
print e
Tagged as: google app engine, python, ネタ
Bookmarks:
みんなのPython
いけないチェリー・パイ