Amazon

ラベル Python の投稿を表示しています。 すべての投稿を表示
ラベル Python の投稿を表示しています。 すべての投稿を表示

2011年11月26日土曜日

PythonでJavaScriptのencodeURIと同じことをやるには?

JavaScriptで言うところのencodeURIと同じことをPythonでやるにはurllib.quote()メソッドを使う。

第一引数に変換したい文字列を、第二引数に変換したい文字コードを設定すること。
# -*- coding: utf-8; -*-

import urllib

print urllib.quote('いろはにほへと')
print urllib.quote(u'いろはにほへと'.encode('euc-jp'))
print urllib.quote(u'いろはにほへと'.encode('shift_jis'))
print urllib.quote(u'いろはにほへと'.encode('iso-2022-jp'))
print urllib.quote(u'いろはにほへと')
print urllib.unquote('%E3%81%84%E3%82%8D%E3%81%AF%E3%81%AB%E3%81%BB%E3%81%B8%E3%81%A8')


但し上記の方法だと、GETリクエストのURIは作りにくいので、urllib.urlencode()というメソッドがある。これはクエリにしたいものをリスト形式で記述して、urlencode()の引数に与えてあげる。(辞書形式の場合は順番は保持されない)


# -*- coding: utf-8; -*-

import urllib

urlprefix = 'http://www.google.co.jp/search?'
keyword = u'Python URIエンコード'
lang = 'ja'
encoding = 'utf-8'
query = [
    ('q', keyword.encode(encoding)),
    ('hl', lang),
    ('lr', 'lang_' + lang),
    ('ie', encoding),
    ('oe', encoding),
]

print urlprefix + urllib.urlencode(query)
 
 
参考元はこちら 
 

2011年10月10日月曜日

Djangoテンプレートの埋め込みタグ

Django埋め込みタグリファレンスより

よく使いそうなものをピックアップ

now

指定したフォーマット文字列にしたがって現在の日時を表示します。
フォーマット文字列中で普通の文字列を使いたければ、バックスラッシュでエスケー プできます。下の例では、"f" が時刻を表すフォーマット指定子として解釈されな いようにエスケープしています。 "o" はフォーマット指定子ではないのでエスケー プしていません:
It is {% now "jS F Y H:i" %}
It is the {% now "jS o\f F" %}
フォーマット文字 説明 出力例
a 'a.m.' または 'p.m.' (Associated Press に合わせるため、'.' が入っている点 が PHP と違います)。 'a.m.'
A 'AM' または 'PM' です。 'AM'
b 3 文字の小文字で表した月名です。 'jan'
B 実装されていません。  
d 月の中の日。 2 桁のゼロ詰めです。 '01' から '31'
D 週の中の日。 3 文字のテキスト形式です。 'Fri'
f 12 時間表記の時と分。ただし、ゼロ分の 場合には表示しません。独自の拡張です。 '1', '1:30'
F 月名を長いテキスト形式で表したものです。 'January'
g 12 時間表記の時。ゼロ詰めはしません。 '1' から '12'
G 24 時間表記の時。ゼロ詰めはしません。 '0' から '23'
h 12 時間表記の時です。 '01' から '12'
H 24 時間表記の時です。 '00' から '23'
i 分です。 '00' から '59'
I 実装されていません。  
j 月の中の日。ゼロ詰めしません。 '1' から '31'
l 週の中の曜日。長いテキスト形式です。 'Friday'
L 閏年かどうかを表すブール値です。 True または False
m 月です。2 桁でゼロ詰めしたものです。 '01' から '12'
M 月です。3 文字のテキスト形式です。 'Jan'
n 月です。ゼロ詰めしません。 '1' から '12'
N Associated Press スタイルの月の省略表記 です。独自の拡張です。 'Jan.', 'Feb.', 'March', 'May'
O グリニッジ標準時からの時差です。 '+0200'
P 時刻です。12 時間表記の時、分、 そして 'a.m.'/'p.m.' です。分がゼロの 場合には省略され、必要に応じて 'midnight' または 'noon' になります。 独自の拡張です。 '1 a.m.', '1:30 p.m.', 'midnight', 'noon', '12:30 p.m.'
r RFC 2822に従ったフォーマットの日時です。 'Thu, 21 Dec 2000 16:01:07 +0200'
s 秒です。 2 桁のゼロ詰めです。 '00' から '59'
S 月の中の日につける 2 文字の序数接尾辞 です。 'st', 'nd', 'rd' or 'th'
t 月の日数です。 28 から 31
T 計算機のタイムゾーン設定です。 'EST', 'MDT'
U 実装されていません。  
w 週の中の曜日です。ゼロ詰めしません。 '0' (Sunday) to '6' (Saturday)
W ISO-8601 に従った年の中の週番号です。 週は月曜日から始まります。 1, 53
y 2 桁の年です。 '99'
Y 4 桁の年です。 '1999'
z 年の中の日 0 から 365
Z タイムゾーンオフセットを秒であらわした ものです。UTC よりも西側のタイムゾーン値 は全て負の値になり、東側の値は常に正に なります。 -43200 から 43200

include

テンプレートをロードして、現在のコンテキストを使ってレンダリングします。あ るテンプレートに別のテンプレートを取り込む ("include") 方法の一つです。
テンプレート名はハードコードされた (引用符で囲った) 文字列でもよく、引用符 は一重でも二重でもかまいません。
以下の例では、 "foo/bar.html" という名前のテンプレートを取り込みます:
{% include "foo/bar.html" %}
次の例では、 変数 template_name に入っている名前のテンプレートを取り込 みます:
{% include template_name %}
取り込まれたテンプレートは、取り込んだ側で使われているコンテキストの下でレ ンダリングされます。下の例では "Hello, John" を出力します:
  • コンテキスト: 変数 person"john" に設定
  • テンプレート:
    {% include "name_snippet.html" %}
  • name_snippet.html テンプレート:
    Hello, {{ person }}
{% ssi %} も参照してください。

cycle

Django 1.0 で変更されました: タグを処理するごとに、指定した文字列や変数を循環して返します。
ループの中では、ループごとに指定した文字列や変数を循環して返します:
{% for o in some_list %}
    <tr class="{% cycle 'row1' 'row2' rowvar %}">
        ...
    </tr>
{% endfor %}
ループの外側では、最初に一意な名前を与えておき、以後はその名前を使います。 例えば:
<tr class="{% cycle 'row1' 'row2' rowvar as rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr>
任意の個数の値を使えます。値はスペースで区切ります。値をクオート (') または二重クオート (") で囲むと、文字列リテラルとして扱います。 クオートされていない値はコンテキスト変数への参照とみなされます。
値をカンマで区切った形式もつかえます:
{% cycle row1,row2,row3 %}
ただし、この構文では、値は全てリテラルテキストとして扱われます。 カンマを使った構文は以前のバージョンとの互換性のために残されています。 新たなプロジェクトでは使わないようにしてください。

for

アレイの各要素に渡ってループします。例えば、アスリート (athlete) のリストを athlete_list で渡して表示するには:
<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
{% for obj in list reversed %} のようにすると、リストに対して逆順のルー プを実行できます。
Django 1.0 で新たに登場しました.
リストのリストにわたってループ処理を行う場合、各サブリストをアンパックして、 個別に名前を割り当てられます。例えば、座標 (x, y) のリストが入った points というコンテキスト変数があり、各座標を出力したい場合には以下のよ うにします:
{% for x, y in points %}
    座標 {{ x }},{{ y }} が登録されています。
{% endfor %}
この方法は、辞書の各要素にアクセスしたい場合にも便利です。例えば、コンテキ スト変数 data に辞書が入っている場合。以下のようにすれば辞書内のキーと 値を表示できます:
{% for key, value in data.items %}
    {{ key }}: {{ value }}
{% endfor %}
for ループは、ループの各回ごとに使える変数を設定します:
変数名 説明
forloop.counter 現在のループ回数番号 (1 から数えたもの)
forloop.counter0 現在のループ回数番号 (0 から数えたもの)
forloop.revcounter 末尾から数えたループ回数番号 (1 から数えたもの)
forloop.revcounter0 末尾から数えたループ回数番号 (0 から数えたもの)
forloop.first 最初のループであれば True になります
forloop.last 最後のループであれば True になります
forloop.parentloop 入れ子のループの場合、一つ上のループを表します 




 

2011年9月6日火曜日

Pythonによるモバイル開発の文字コード2

前回に引き続き、Pythonの文字コードで引っかかったお話。
今回はPythonというよりも、Opensocial APIのgadgets.io.endoceValues()メソッドのお話。

Opensocialのアプリを作成している際
HTML側のメタタグで
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=Shift_JIS" />
と指定した上で
<input type="button" onclick="postSubmit()"/>
と書く。

この時、JavaScriptの
function postSubmit(){
...
var value1="";
var value2="";
params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
params[gadgets.io.RequestParameters.AUTHORIZATION] = gadgets.io.AuthorizationType.SIGNED;
var post_data{
'key1':value1,
'key2':value2,
} 
params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(post_data);
と書いてPOSTリクエストをすると、
gadgets.io.endoceValues()が有効になり、
POSTされる文字コードは,入力オブジェクトを URL エンコードしたデータ文字列に変換される。
POST のデフォルト エンコードは application/x-www-form-urlencoded でありShift-JISではない。
 
よって、Python側で受けるとき
self.request.charset="Shift-JIS"
をセットしてしまうと返って文字化けを引き起こしてしまう。
ということで、JavaScriptからPOSTされた場合には、self.request.charset="Shift-JIS"
は呼ばないようにif文を書いてあげる。
userAgent=detectAgent(self.request.user_agent)
if userAgent != 'pc':
 
ここでdetectAgentメソッドは独自メソッドで、self.request.user_agent(HTTP_USER_AGENT)に入っている文字列からpcかsmartphoneかmobileかを判定する。
def detectAgent(userAgent):
    if userAgent.startswith('DoCoMo'):
        return 'mobile'  
    elif userAgent.startswith('KDDI-'):
        return 'mobile'
    elif userAgent.startswith('SoftBank'):
        return 'mobile'
    elif userAgent.startswith('Vodafone'):
        return 'mobile'
    elif userAgent.startswith('Mozilla/5.0 (iPhone'):
        return 'smartphone'
    elif userAgent.startswith('Mozilla/5.0 (Linux'):
        return 'smartphone'
    else:
        return 'pc' 
あまりスマートな書き方じゃないけど、とりあえず暫定版ということで。

2011年9月3日土曜日

Pythonによるモバイル開発の文字コード1


携帯Webサイトを作る際、文字コードがShift-JISにするのが一般的。
このWebサイトからフォームで日本語を POSTする場合、
Python側で受取る時に注意が必要。
通常PythonではUTF-8で受取るため、文字化けを起こす。

そこで、self.request.get('xxx)とする前に
self.request.charset('Shift-JIS')で文字コードを指定する。
こんな感じ

def post(self):
        self.request.charset="Shift-JIS"
        self.request.get('hoge')

Pythonでテスト駆動開発2 -Aptanaからnoseの実行-

前回noseを使って、Pythonでテストを実行する方法について書いた
今回はAptana(Eclipse)からPythonのテストフレームワークのnoseを使用する方法をまとめます。

テストするクラスとテストコードは前回のtest1.py, test2.py

test1.py, test2.pyでは使用していないけど、
テストコードで以下の一文があると、まずライブラリが見つからないとAptana(Eclipse)から怒られる。
from nose.tools import ok_, eq_

ということで解決策
1. プロジェクトを右クリックしてプロパティ
2. PyDev - PYTHONPATHにnoseのライブラリフォルダを追加
 (C:\Python27\Lib\site-packages\nose-1.1.2-py2.7.egg)

テストの実行は
test2.pyの最後に
if __name__ == '__main__':
    import nose
    nose.main()
の記述を入れると、pythonの実行ファイルと認識されて、Eclipseの実行設定ファイルで読み込めるようになる。

ちなみに実行設定ファイルは以下の通り。




で、上記を実行すると
Failure: ImportError (No module named testdata) ... ERROR
Failure: ImportError (No module named sancho.unittest) ... ERROR
Failure: ImportError (No module named sancho.unittest) ... ERROR
Failure: ImportError (No module named sancho.unittest) ... ERROR
Failure: ImportError (No module named sancho.unittest) ... ERROR
Failure: ImportError (No module named sancho.unittest) ... ERROR
Failure: ImportError (No module named sancho.unittest) ... ERROR
Failure: ImportError (cannot import name AES) ... ERROR
Failure: ImportError (No module named testdata) ... ERROR
という結果になる。

このプロジェクト内のテスト(class Testxxx)を全部実行するため、 本来テストしたいもの以外も実行されてしまっている。

ということで、必要なテストだけを実行する場合は--tests=NAMEオプションを付けて実行する。
通常はnose.main()にargs=["-v","--name=tests2.py"] を渡して
test.py
if __name__ == '__main__':
    import nose
    nose.main(args=["-v","--name=tests2.py"] )
とするのが一般的のようだけど、何故か引数が有効にならない。
Aptana(Eclipse)を使用する場合はmainへの引数は実行設定ファイルで設定するようです。



で実行するとverboseモードになって(test.xxx ... okみたいな)テスト結果が表示される。

test2.TestDollar.Multiplicaton_test ... ok

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK


○注意点
class名はTestxxxとTestで始めること
メソッド名はyyy_testとすること

2011年9月1日木曜日

Pythonでテスト駆動開発1

Pythonでテスト駆動開発を行うためのツールとしてunittestとnoseがある。参考はこちら
unittestでは必要なクラスを継承するなど、いくつかの決まりごとがあるが、noseでは正規表現 "(?:^|[b_.-])[Tt]est" にマッチしたクラス・関数が実行されるようになっているため、ルールがシンプル。

noseのインストール
% easy_install nose
 
○テスト実行時
テストの実行は次の2種類があります。
  1. nosetests コマンドでテスト対象のファイルやディレクトリを指定
  2. テストスクリプト内でテストを実行
(1) の場合は、nosetests の引数にテストファイルを指定、あるいは -w オプションでテストファイルを含んだディレクトリを指定することでテスト実行します。
(2)の場合は、テストプログラム内で nose.main 関数を呼び出すようにします。
def test_compare():
    assert 1==1
if __name__ == '__main__':
    import nose
    nose.main()


テストクラスが入っているディレクトリ内で 
% nosetests 
 
サンプルクラス(test1.py)
#-*- coding: utf-8 -*-

class Dollar():

    def __init__(self, amount=0):
	self.amount = amount

    def times(self, multiplier):
	self.amount *= multiplier
テストコード(test2.py)
#-*- coding: utf-8 -*-

from test1 import Dollar
from nose.tools import *

class TestDollar():

    def testMultiplicaton(self):
        five = Dollar(5)
        five.times(3)
        eq_(15, five.amount)
成功すると
---------------------
Ran 1 test in 0.047s

OK
失敗すると
======================================================================
FAIL: test2.TestDollar.testMultiplicaton
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\nose-1.1.2-py2.7.egg\nose\case.py", line 1
97, in runTest
    self.test(*self.arg)
  File "C:\temp\test2.py", line 11, in testMultiplicaton
    eq_(10, five.amount)
  File "C:\Python27\lib\site-packages\nose-1.1.2-py2.7.egg\nose\tools.py", line
31, in eq_
    assert a == b, msg or "%r != %r" % (a, b)
AssertionError: 10 != 15

----------------------------------------------------------------------
Ran 1 test in 0.562s

FAILED (failures=1)
○オプション

verbose モード

「-v」 オプションを有効にすると、verbose モードでテストが実行されます。-vvv のように v を重ねると、より verbose なメッセージが表示されます。

pdb 対応


「--pdb」 オプションを有効にすると、テストプログラムで例外が起きると同時に、pdb が起動し、デバッグモードに入ります。

「--pdb-failures」オプションを有効にすると、テストプログラムでテストが失敗すると同時に、pdb が起動し、デバッグモードに入ります。

パターンマッチ


「-i 正規表現パターン」オプションを有効にすると、正規表現にマッチしたファイルのみがテスト実行されます。

「-e 正規表現パターン」オプションを有効にすると、逆に正規表現にマッチしたファイルは実行されなくなります。


○さらに詳しく理解するには
nose まとめ 1

2011年8月23日火曜日

AttributeError: 'tuple' object has no attribute

Pythonにて。


body="anonymous"
sjis(body)

def sjis(body)
        if isinstance(body, str):
            body = body.replace(u'\u00a6', u'\u007c'), #broken bar=>vertical bar
            body = body.replace(u'\u2014', u'\u2015'), #horizontal bar=>em dash

上のコードを実行すると
   body = body.replace(u'\u2014', u'\u2015'), #horizontal bar=>em dash
AttributeError: 'tuple' object has no attribute 'replace'
エラーが出る。
タプルは値を変更できないのに変更しようとしているからエラー、という意味なのだけど
そもそもタプルなんか使ってないのに何でだろう?と考えること10秒。

replaceの行にカンマが入ってんじゃん。。。
            body = body.replace(u'\u00a6', u'\u007c'), #broken bar=>vertical bar
            body = body.replace(u'\u2014', u'\u2015'), #horizontal bar=>em dash

このカンマを消したら見事エラーも消えましたとさ。

2011年7月31日日曜日

フィーチャーフォンのアプリを作る際の注意点





Perlで作るモバイルサイトのコツ  より

・文字コード

携帯用サイトの構築を行う場合にはWebアプリケーション内部ではUTF-8を使用し、出力の際にはShift_JISを使用できるようにUTF-8とShift_JISを相互変換する処理を実装する必要があります。

・キャリアに応じたXHTMLの出力

キャリアの仕様


・セッション管理

セッション管理は、ユーザーからのリクエストごとに一意のID(セッションID)を発行し、常にそのIDをリクエストに含ませることで行います。 PCであればクッキーを使用してセッションIDをブラウザに保持しておくことができますが、前述したとおり携帯ではクッキーを使用できる端末が限られるた め、画面遷移時にセッションIDを直接リクエストのパラメータとして渡す必要があります。

 パラメータを引き継ぐ必要のある遷移のパターンは以下の3つです。

  1. リンクによる遷移
  2. フォームによる遷移
  3. リダイレクトによる遷移

 

○PythonでUser Agentの値を取得する

http://d.hatena.ne.jp/perezvon/20080919/1221844404

logging.info(dir(self.request))でuser_agentというプロパティ

# it works!
self.request.user_agentを使う
 
○User Agentの値を表示するPythonサンプル 
 
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

class Receiver(webapp.RequestHandler):
  def get(self):
    self.response.out.write(
    """
    <html>
      <body>
        <b>%s</b>
      </body>
    </html>
    """ % self.request.user_agent)
    
    
application = webapp.WSGIApplication([
  ("/", Receiver)
], debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main() 
 
 
Google App Engine for Pythonでのセッション管理の実装
セッション管理部分のみ抜粋

def getSession(request):
# クッキーのセッションIDとmemcache/Datastoreのセッション情報を照合する関数
# リクエストハンドラーのself.requestを引数として受け取る

    # クッキーからセッション番号を取得
    sid = request.cookies.get('SID', '')
    if sid:
        # まずmemcacheにそのセッション番号があるか探す
        session = memcache.get(sid)
        if session:
            return session

        # memcacheになければDatastoreから探す
        else:
            session = Session.get(sid)
            if session:
                # Datastoreにあればmemcacheにも入れておく
                memcache_key = str(session.key())
                memcache_value = {
                    'user' : {
                        'key' : str(session.user.key()),
                        'name' : session.user.name,
                    },
                }
                memcache.add(key=memcache_key, value=memcache_value, time=3600)

                return session
 
Google App EngineでDjangoのテンプレートエンジンを使う
import wsgiref.handlers
from google.appengine.ext import webapp

#テンプレートを使用するため、下記二つのimportを追加
import os
from google.appengine.ext.webapp import template

class MainHandler(webapp.RequestHandler):

    def get(self):
        #テンプレートに渡す果物リスト
        fruits = ('みかん','りんご','バナナ','パイナップル','スイカ')
        #テンプレートに渡す文字列
        j_str = '日本語文字列';
        
        #連想配列にテンプレートに渡す値を設定
        #テンプレートからは、連想配列のキーでアクセスする。
        template_values = {'list' : fruits,
                            'str_value' : j_str}
        
        #使用するテンプレートを指定
        path = os.path.join(os.path.dirname(__file__), 'template/index.html')
        #レンダリングを実行し、結果をレスポンスとしてブラウザに返却
        self.response.out.write(template.render(path, template_values))

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


if __name__ == '__main__':
    main()
 
アプリケーションの構成はapp.yamlと同階層フォルダにtemplateを作って、その下にindex.htmlテンプレートファイルを作る
template/index.html
app.yaml
index.yaml
main.py 

Amazon3