今さらいれたよ 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
インストール実行画面
インストールされた場所
$ 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のトークンについての覚え書き。
最近では、Facebook や Twitter の OAuth認証を介して、ログイン機能を提供するWebサービスが大分増えてきましたよね。しかも、ID/PASSでのログインとは別に、おまけ程度にあるのではなく、Facebook or Twitter でしかログインできないなんてサービスもちらほらあります。
FacebookのOAuth認証を通して手に入るユーザーのアクセストークンは、ユーザーがパスワードを変更したタイミングとかで無効になるので、認証するたびに新しいトークンになってたら、差し替えておいた方が良いんじゃないかなという話を長ったらしくします。
Facebook のOAuth認証のフロー
割と適当に書いたシーケンス図(ぽい)もので確認しましょう。
アプリケーションの要件によって、異なると思いますが、Webアプリケーションだったら、大体こんなフローになると思います。
これでユーザーはApplicationにログインできるようになります。2回目以降は、7のアクセストークンが戻ってきたタイミングで、DBに問い合わせを行い、アクセストークンが既に存在していれば、ログイン状態になるといった具合です。
アクセストークンの有効期限が切れる
- Applicationが求めるスコープに「offline_access」を入れてあげないと、割と1時間位でアクセストークンが有効期限切れなる。
- Applicationにログインしている状態で、有効期限切れが起きるとApplicationからFacebookのGraphAPIが利用できなくなります。ex) ウォールへの投稿ができない!!!
パスワード変更すると無効になる
- パスワードを変更するとアクセストークンが無効になります。
- つまりApplicationにログインしている状態で、別のブラウザでFacebook開いてパスワードを変更したら、ApplicationではGraphAPIを利用できなくて、アババババとなります。
どうするのが良いか
アクセストークンが切れたら、ユーザーに再認証してもらう意外に、正しいアクセストークンを手に入れる術はありません。なので再認証してもらうための案を考えてみましょう。
- ログインの中に、アクセストークンの更新処理を組み込む
- 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 について
こんな現象に遭遇したので覚え書き
そもそも
- 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は、エスケープされて下記のように出力される事を期待するだろう。
<a href="http://example.com">hoge</a>
ところが、これは期待に反して素の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の対象にならないのだ。
- バージョン0.5 からそのような仕様になったらしい
で回避策は?
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 とかタイポしてたので修正。。。はずかし