今さらいれたよ vundle プラグイン

去年から知ってはいたものの、なんかめんどくさがってやってなかったシリーズ(別に続かない)。んで実際入れたら、やっぱ便利だたよ。。。簡単に導入もできるからもっと早くやっとけばよかた。

インストール

$ mkdir ~/.vim/bundle
$ git clone git://github.com/gmarik/vundle.git ~/.vim/bundle/vundle

.vimrcの設定

下記内容を追記

set nocompatible               " be iMproved
filetype off                   " required!
set rtp+=~/.vim/bundle/vundle/
call vundle#rc()
" let Vundle manage Vundle
" required! 
Bundle 'gmarik/vundle'
" My Bundles here:

" githubの特定のリポジトリからインストール
Bundle 'tell-k/vim-browsereload-mac'
Bundle 'fholgado/minibufexpl.vim'

" github の "vim-scripts" のアカウントが持ってるリポジトリ
" からインストール(https://github.com/vim-scripts/)
Bundle 'L9'
Bundle 'FuzzyFinder'
Bundle 'python_fold'

filetype plugin indent on     " required! 

プラグインのインストール

vimを開いて下記コマンドを実行すればインストール開始

:BundleInstall

コマンドラインからも下記のようにすればインストールできる

$ vim +BundleInstall +qall

インストール実行画面

f:id:tell-k:20120126205806p:plain

インストールされた場所

$ tree -L 1 ~/.vim/bundle/
~/.vim/bundle/
├── FuzzyFinder
├── L9
├── minibufexpl.vim
├── python_fold
├── vim-browsereload-mac
└── vundle

で完了。ふぉぉぉ簡単!w

余談

  • vundleはgitおよびgithubに依存している
  • BundleInstallすれば、一気にインストールしてくれるので、バージョン管理する必要がない。
  • なので ~/.vim/bundle以下はignore設定でおk

以上ヒゲメガネでしたー。

辞書のマージ(同一キーの値は加算) 其の二

さてこの前下記のようなエントリを書いたら、エアリプライが大好きな、戦国大名の人が「同一のキーの値の加算」だったら、Counterオブジェクトでいいのにねぇ。。。みたいな事を、そうエアリプライで教えてくれましたw エアリプライでw ありがとうございます。

こんな感じ

#!/usr/bin/env python
#-*- coding:utf8 -*-
from collections import Counter

d1 = {'a': 1, 'c': 3, 'b': 2}

def merge_dict_add_values(d1, d2):
    return dict(Counter(d1) + Counter(d2))

print merge_dict_add_values(d1, d1) #=> {'a': 2, 'c': 6, 'b': 4}

おぉちゃんと加算されてるw

ただ加算した結果が0以下になると以下のようにキーが消えてしまうので注意

d1 = {'a': 1, 'c': 3, 'b': 2}
d2 = {'a': 1, 'c': -3, 'b': -3}

#加算した結果が0以下になるとキーごと消えてしまう
print merge_dict_add_values(d1, d2) #=> {'a': 2}

#前回のだとキーは消えない
print merge_dict(d1, d2, lambda x, y: x + y) #=> {'a': 2, 'c': 0, 'b': -1}

ぶっちゃけ「collections」モジュール自体知らんかった。。。

おまけ

コレ凄いw 小宮さん凄いw 戦国大名の御館様好きで、Mac持ってる人は、実行してみるといいよ!

$ say -v cellos e ma gah wa yah cut tar yah cut tar yah cut tar e ma gah wa yah cut tar oh yah cut tar sammah

ref: https://twitter.com/tk0miya/status/162086627764731905

@tk0miya++

FacebookのOAuthのアクセストークンについて

FacebookのOAuthのトークンについての覚え書き。

最近では、FacebookTwitter の OAuth認証を介して、ログイン機能を提供するWebサービスが大分増えてきましたよね。しかも、ID/PASSでのログインとは別に、おまけ程度にあるのではなく、Facebook or Twitter でしかログインできないなんてサービスもちらほらあります。

FacebookのOAuth認証を通して手に入るユーザーのアクセストークンは、ユーザーがパスワードを変更したタイミングとかで無効になるので、認証するたびに新しいトークンになってたら、差し替えておいた方が良いんじゃないかなという話を長ったらしくします。

Facebook のOAuth認証のフロー

割と適当に書いたシーケンス図(ぽい)もので確認しましょう。
アプリケーションの要件によって、異なると思いますが、Webアプリケーションだったら、大体こんなフローになると思います。

f:id:tell-k:20120125002219p:plain

これでユーザーはApplicationにログインできるようになります。2回目以降は、7のアクセストークンが戻ってきたタイミングで、DBに問い合わせを行い、アクセストークンが既に存在していれば、ログイン状態になるといった具合です。

アクセストークンの有効期限が切れる

  • Applicationが求めるスコープに「offline_access」を入れてあげないと、割と1時間位でアクセストークンが有効期限切れなる。
  • Applicationにログインしている状態で、有効期限切れが起きるとApplicationからFacebookのGraphAPIが利用できなくなります。ex) ウォールへの投稿ができない!!!

パスワード変更すると無効になる

  • パスワードを変更するとアクセストークンが無効になります。
  • つまりApplicationにログインしている状態で、別のブラウザでFacebook開いてパスワードを変更したら、ApplicationではGraphAPIを利用できなくて、アババババとなります。

どうするのが良いか

アクセストークンが切れたら、ユーザーに再認証してもらう意外に、正しいアクセストークンを手に入れる術はありません。なので再認証してもらうための案を考えてみましょう。

  • ログインの中に、アクセストークンの更新処理を組み込む
    • 毎回ログインする時はFacebook認証をするので、そのついでに、あたらしいアクセストークンに差し替えてしまう。
    • アクセストークンでDBを検索
    • なかったら8で取得するFacebookのプロフィールIDをキーにDB検索する
    • もしあればデータあれば、それは「1度ログインして、且つアクセストークンが変わった」データなので、ついでにアクセストークンも新しいものに変更してやる
    • まぁ普通に皆やってるんだろうけど、プロフィールIDとか付帯情報はとっておきましょうねという事。
  • Facebookの接続解除、再接続機能を実装する。
    • 単純にDB上のデータを削除して、再度いちから認証にしてもらう。

なんか、うまくまとめられないので今日はここまで

辞書のマージ(同一キーの値は加算)

辞書のマージで、同一のキーが存在してたら、値を加算するような事がしたかった。

{'a': 1, 'c': 3, 'b': 2} + {'a': 1, 'c': 3, 'b': 2}  #=> {'a': 2, 'c': 6, 'b': 4}

単にマージするだけだったら、dict.updateで出来るけど、同一キーの場合に値を加算するってのはどうしたらいいか良くわからんかった。

前提として、辞書の値は、数値だった。

こうやった

たしかスタックなんちゃらフローで探して、割とベタにこんな風にやった。URLは忘れてもうた。

def merge_dict(d1, d2, func=lambda x, y: y):
    d1 = d1.copy()
    d2 = d2.copy()
    for k, v in d2.iteritems():
        d1[k] = func(d1[k], v) if k in d1 else v
    d2.update(d1)
    return d2

d1 = {'a': 1, 'c': 3, 'b': 2}
merge_dict(d1, d1)  #=> {'a': 1, 'c': 3, 'b': 2}   通常のupdateと結果は一緒
merge_dict(d1, d1, lambda x, y: x + y) #=> {'a': 2, 'c': 6, 'b': 4} 

余談

  • なんかもっと簡単にやる方法あったらいいなぁ
  • なんでcopyでやったのかは忘れた。

以上おわり

Flask に manage.py が欲しい

そういえば、どこかでタイトルのような話をどこかで見た気がするけど忘れた。

Djangoでは、runserverや、カスタムでコマンドを定義したいときに、manage.pyを使うらしいが、それと似たような事ができる、エクステションもある。Flask-Scriptというヤツだ。

http://packages.python.org/Flask-Script/

以下覚え書き

インスール

pip install Flask-Script

簡単な例

  • manage.pyとかいうファイル名でこんな感じのを作る
  • from flasksample import app は 自分で作ったりアプリで、app はFlask()のインスタンスとする
from flaskext.script import Server, Manager
from flasksample import app

manager = Manager(app)

if __name__ == "__main__":
    manager.run()

この状態で、下記のように実行すると既にデフォルトで「shell」と「runserver」が使えるようになってる。

$ python manage.py 
  shell      Runs a Python shell inside Flask application context.
  runserver  Runs the Flask development server i.e. app.run()

runserverを実行したい場合はこう実行する。

$ python manage.py runserver
 * Running on http://127.0.0.1:5000/
 * Restarting with reloader

カスタムコマンドを作る

大きく分けてやり方は二つある。

  • それ用の関数デコレータを利用する
  • Commandクラスを継承して新しいコマンドを作る

それ用の関数デコレータを利用する。

manager = Manager(app)

@manager.command
def haruo():
    """ print Haruo is World """
    print 'Haruo is World'

こうすると、haruoコマンドが追加される。関数のdocstringがコマンド一覧を表示したときの説明に使われる。

$ python manage.py 
  haruo      print Haruo is World
  shell      Runs a Python shell inside Flask application context.
  runserver  Runs the Flask development server i.e. app.run()
$ python manage.py haruo
Haruo is World

関数に対して引数をわたすにはこう。

@manager.command
def haruo(name):
    """ print Haruo is World """
    print 'Haruo is '  + name
$ python manage.py haruo Gold
Haruo is Gold

オプション付きのコマンドにするときはoptionデコレータを使う

@manager.option('-s', '--semeru', help='SEMERU HITO')
@manager.option('-u', '--ukeru', help='UKERU HITO')
def boyslove(semeru, ukeru):
    """ boyslove """
    print semeru + ' x ' + ukeru
$ python manage.py boyslove -s oyakata -u podhmo
oyakata x podhmo

このとき「-h」でヘルプ内容が確認できる。

$ python manage.py boyslove -h
usage: manage.py boyslove [-h] [-u UKERU] [-s SEMERU]

boyslove

optional arguments:
  -h, --help            show this help message and exit
  -u UKERU, --ukeru UKERU
                        UKERU HITO
  -s SEMERU, --semeru SEMERU
                        SEMERU HITO

Commandクラスを継承して新しいコマンドを作る

Commandクラスを継承して作ることもできる。上のboysloveコマンドをこの方式で作り直すとこんな感じ。

from flaskext.script import Manager, Command, Option
from flasksample import app

manager = Manager(app)

class BoysLove(Command):
    """ boyslove command """
   
    option_list = (
        Option('-s', '--semeru', dest='semeru', required=True, help='semeru hito'),
        Option('-u', '--ukeru',  dest='ukeru', required=True, help='ukeru hito'),
    )
    def run(self, semeru, ukeru):
        print semeru + ' x ' + ukeru

manager.add_command("boyslove", BoysLove())

if __name__ == "__main__":
    manager.run()


ややこしいバッチ処理を作るときは、Commandクラスを継承した方がよいかも。

以上おわり。

Flask の Jinja2 の autoescape について

こんな現象に遭遇したので覚え書き

そもそも

エスケープされない

autoescapeという名前から、全てのテンプレートの処理で自動エスケープされると思っていたが、そうじゃなかった。
たとえばこんなケースだ。

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')
def index():
   return render_template('index.tmpl',  rawhtml='<a href="http://example.com">hoge</a>')

if __name__ == '__main__':
    app.run()

これをブラウザで出力したとき、rawhtmlは、エスケープされて下記のように出力される事を期待するだろう。

&lt;a href=&#34;http://example.com&#34;&gt;hoge&lt;/a&gt;

ところが、これは期待に反して素のaタグが出力されてしまう。

<a href="http://example.com">hoge</a>

なぜ?

render_templateに渡してるテンプレートファイル名を見てほしい。

index.tmpl

テンプレートファイルになんだか、見慣れない独自の拡張子を使っている。これが原因だ。
テンプレートファイルをリネームして「index.html」に変えて、render_templateの引数を「index.html」に変える。
すると、正しくエスケープされる。

どういうこと?

結論を言うと「autoescape」対象となるテンプレートの拡張子は決まっている。

「flask/app.py」のソースの下記箇所がそれに該当する。

 634     def select_jinja_autoescape(self, filename):
 635         """Returns `True` if autoescaping should be active for the given
 636         template name.
 637 
 638         .. versionadded:: 0.5
 639         """
 640         if filename is None:
 641             return False
 642         return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))

「.html」「.htm」「.xml」「.xhtml」しかautoescapeの対象にならないのだ。

で回避策は?

autoescape対象となる拡張子を追加するには、ソースにベタで拡張子が書いてるのを見ても分かる通り、あまり簡単ではなさそうだ。「.html」なりで統一する事をお勧めします。

けど追加したい、どうしても追加したい、という言う人は、下記のように強引に変える手もあるが、ものすごくバッドノウハウ臭がするのでオススメしません。

def select_jinja_autoescape(filename):
        """ jinja2のautoescape対象の判別メソッドを差し替える
        :refs: Flask.select_jinja_autoescape
        """
        if filename is None:
            return False
        return filename.endswith(('.html', '.htm', '.xml', '.xhtml', '.tmpl'))

app.jinja_env.autoescape = select_jinja_autoescape

ちなみに

  • autoescapeが効いているテンプレート上で、部分的にautoescapeを止めたい時には下記のようにすればよい
{% autoescape false %}

    {{ rawhtml }}

{% endautoescape %}

github のプロジェクトにSphinxドキュメントを良さげな感じにおきたい 其の二

昨日の続き。色々なんか眠いので端折るでしかし。

面倒な事

昨日までの話だと、こんな感じに、ブランチを切り替えながら作業しなければなりません。

  • Sphinxドキュメントを編集する -> gh-pagesブランチに切り替えて作業
  • アプリを開発する -> masterブランチに切り替えて作業

これはめんどくさいですね。そこで「git submodule」を利用して、masterブランチ上に、gh-pagesのブランチをsubmoduleとして持ってしまいましょう。

これにより、ブランチを切り替えることなく、ドキュメントを編集する事が可能になります。

git submodule をつかう

git submoduleは、svnの外部リポジトリのような事ができます。実際に下記コマンドを入力してみましょう。

git submodule add -b gh-pages `git remote -v|grep fetch|awk '{print$2}'` docs
  • `git remote -v|grep fetch|awk '{print$2}'` では github.comのcloneするときのURL?が取得できます。つまり今いるリポジトリですね。 ex) git@github.com:tell-k/sphinx-doctest.git
  • gh-pages ブランチを、docsとういうディレクトリ名でsubmoduleとして登録します。

実行した後は、こんな感じに、READMEしかなかったmasterブランチに、docsディレクトリ、そしてgh-pagesのSphinxドキュメントのファイル群が確認できます。

.
├── README
├── .gitmodules
└── docs
    ├── Makefile
    ├── _sources
    ├── _static
    ├── build
    ├── genindex.html
    ├── index.html
    ├── objects.inv
    ├── search.html
    ├── searchindex.js
    └── source

また「.gitmodules」というファイルが出来てると思います。この中身を除くと、submoduleの設定内容が確認できます。

[submodule "docs"]
       path = docs
       url = git@github.com:tell-k/sphinx-doctest.git

これで対応は完了です。ドキュメントを更新するときにbranchをいちいち切り替えずに作業を始められそうですね。

cloneした時のsubmoduleの初期化

他のマシン環境でも、このgithubのリポジトリを利用したいと思います。
普通に「git clone」で取得すると思いますが、最初は「submodule」の設定はなにもない状態だったります。

なのでcloneしたら、初期化してあげましょう。これで上記構成と同じ物が作られます。

 git submodule init
 git submodule update

余談

  • Sphinxドキュメントが前提なのであれば、最近はRead The Docsの方が主流になりつつあると聞いたので、そのへんのやり方も次回あたりまとめます。

追記(2012/01/20)

  • Read The Docs を Read The Dog とかタイポしてたので修正。。。はずかし