Mac で Vim とクリップボードを共有
Vim で OSのクリップボードを共有する時は、.vimrcに 「set clipboard=unnamed」と書けば良い。
set clipboard=unnamed
これで vim上でヤンクしたものとかが、Command + V とかで張りつけできたりする。
ところが、ある日ふと家の Mac OS X(Lion)で出来てない事にきづいた。vimrcを見直してみたけど、特に問題なさそうというか、会社でも同じvimrcの設定使ってるのに旨くいってるし。。。
良くわからないのでググったら、答えが見つかった。
なんとデフォルトでインストールされてるvimだと 「-clipboard」て書いてあって、つまり無効。F○○K!!
~$ /usr/bin/vim --version VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Jun 24 2011 23:09:22) Compiled by root@apple.com Normal version without GUI. Features included (+) or not (-): -arabic +autocmd -balloon_eval -browse +builtin_terms +byte_offset +cindent -clientserver -clipboard +cmdline_compl +cmdline_hist +cmdline_info +comments .. ..
vimのインストール
というわけで vimのインストールしますお。 macportsとか使い方忘れたので、情弱な僕はhomebrewでインストール。。。。と探したら無い。。。。どうやら提供されていないようだ。。。野良Formulaという選択肢も考えたけど、なんかみんなつまづいてるから危険
~$ brew search vim macvim vimeo-downloader vimpager vimpc
という訳で、素直にソースからインストール。最近のvimはMercurialのリポジトリを公開しているので、それを利用するのが多分一番手っ取り早い。パッチもあたってる状態なので、最新のものがインストールできる。
hg clone https://vim.googlecode.com/hg/ vim cd vim hg update vim73 make clean ./configure --prefix=/usr/local --enable-multibyte --enable-xim --enable-fontset --enable-rubyinterp --enable-perlinterp --enable-pythoninterp --with-features=huge --disable-selinux make make install
これで大体、僕が必要そうなものはインストールされる。結果はこんな感じ
$ vim --version VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Feb 16 2012 21:56:06) MacOS X (unix) version Compiled by tell_k@tell-k-mackbookair.local Huge version without GUI. Features included (+) or not (-): +arabic +autocmd -balloon_eval -browse ++builtin_terms +byte_offset +cindent -clientserver +clipboard +cmdline_compl +cmdline_hist +cmdline_info +comments .. ..
ちゃんと「+clipboard」になってますね。めでたし。めでたし。 良かったね > おれ
Mac で mkdpreview-vim を使って reST のプレビューを見る
mkdpreview-vim は Markdown記法で書かれたテキストを書きながら、別ウィンドウ上でプレビューも一緒に確認できる便利プラグイン。
Markdown以外に、reST と textile にも対応している。
これを利用してMacOS X 上で reSTructuredTextのプレビューを見てみる。
以下 プレビューを表示するまで。
環境
- MacOS X
pyqtのインストール
- 時間かかるんで最初にインストールしておく。
- 面倒なのでhomebrewでまるっとインストール
$ brew install pyqt
- Qt と sit と PyQT4 を一気にインストールするから、多分 20~30分くらい時間かかる。
- インストールが完了したら、PyQTのある場所にパスを通す。
$ export PYTHONPATH=/usr/local/lib/python
Sphinxのインストール
- reSTをプレビューするためには、docutilsが必要です。Sphinxはdocutilsに依存してるのでpipでインストールすると、Sphinxとdocutilsが一緒にインストールされる。
- なんでわざわざSphinxまで入れるのかというと、Sphinxがデフォルトで持ってるディレクティブを利用したいから。
$ pip install Sphinx
vimプラグインのインストール
- vundleを利用して、必要なプラグインをインストール。vundleが何か分からない人は下記を参照してください。
- .vimrc に下記のように書いて、BundleInstallを実行する。
Bundle 'mattn/mkdpreview-vim' Bundle 'mattn/webapi-vim'
$ chmod u+x ~/.vim/bundle/mkdpreview-vim/static/mkdpreview.py
mkdpreview.vimをいじる。
#変更前 \ "data" : join(getline(1, line('$')), "\n") #変更後 \ "data" : join(getline(1, line('$')), "\n"), \ "type" : 'rst'
注意: 多分そのうち 正式にtypeを指定する方法は変わると思います。
追記(2012/02/13)
- 最新版では type値に関しては、filetypeを自動で読み取って勝手に指定しくれるようになりました。上記修正は必要ありません :)
- 下記のようになってました。
function! s:update_preview() let ret = http#post('http://localhost:8081/', { \ "data" : join(getline(1, line('$')), "\n"), \ "type" : &filetype \}) echo ret.content endfunction
さぁプレビュー
- こんなrstファイルを用意してvimで開く
.. contents:: 目次 The Road of the Drillbits =============================== 第1章 どりるびっつの仲間 --------------------------------- .. code-block:: python print "The Fellowship of the Drillbits" 第2章 二つのどりるびっつ --------------------------------- .. code-block:: python print "The Two Drillbits" 第3章 どりるびっつの帰還 --------------------------------- .. code-block:: python print "The Return of the Drillbits"
- vimを開いて下記コマンドを実行
:MkdPreview!
- するとおもむろにウィンドウが立ち上がりプレビューがこんな感じでみれる!
- 以後は、「:w」で保存すれば勝手にプレビューが更新される。うまく更新されなかったら「:MkdPreview」と打つと良い。
code-blockディレクティブとか使いたい。
- 上の画像では、「.. code-block::」の所で「Unknown directive type "code-block".」という感じで、そんなディレクティブねーよとおこられる
- これはそもそも「.. code-block::」が、docutilsのディレクティブではなく、Sphinxに付属するディレクティブだから。Sphixはインストール済みなので、Sphinx付属のディレクティブが利くようにまたちょっといじる。
「~/.vim/bundle/mkdpreview-vim/static/plugin/mod_rst.py」というファイルを開いて下記のよう2行追加する。
from docutils.core import publish_parts + from sphinx.directives.other import * + from sphinx.directives.code import *
- これでもう一度、ファイルを開き直して、「MkdPreview!」を実行すると、無事にエラーが無くなる
以上おわり。
bpmappers 便利!
今日は bpmappers というライブラリが、すごく便利で感動したという事を書く
良くJSONを返すようなAPIを作るときに、オブジェクトの必要な部分だけを、辞書に変換してからJSONにするなんて事をすると思う。オブジェクトのプロパティが、数値やテキストなどリテラルなものであれば良いが、別のオブジェクトだったりすると、そもそもオブジェクトから辞書への変換は面倒くさそうだ。
これをよしなにやってくれるのがbpmappersである。詳しくはドキュメントを見てもらうとして、簡単な例を紹介しようと思う。
前提
まず SQLAlchemyで下記のようなモデルがあるとする。Entry と Comment は 1:N の関係にある。
# declare models class Entry(Base): __tablename__ = 'entries' id = Column(Integer, primary_key=True) text = Column(String) created_at = Column(DateTime, default=datetime.now, nullable=False) comments = relationship('Comment') class Comment(Base): __tablename__ = 'comments' id = Column(Integer, primary_key=True) entry_id = Column(Integer, ForeignKey('entries.id'), nullable=False) text = Column(String) created_at = Column(DateTime, default=datetime.now, nullable=False)
このように定義すると「Entry.comments」は下記のようにCommentオブジェクトのリストとして取得できる。
# select data entries = Entry.query.all() for entry in entries: for comment in entry.comments: print ' comment.id: ' + str(comment.id) print ' comment.text: ' + comment.text print ' comment.created_at: ' + str(comment.created_at)
この入れ子になったオブジェクト構成をbpmappersを使って簡単にJSONにしてみる。
Mapperの定義
# declare mappers class CommentMapper(Mapper): id = RawField() entry_id = RawField() text = RawField() created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S')) class EntryMapper(Mapper): id = RawField() text = RawField() created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S')) comments = ListDelegateField(CommentMapper) #convert to json print json.dumps(EntryMapper(Entry.query.all()).as_dict(), indent=2)
これで終わり。 Mapperを定義するだけ。 あとはas_dict()でよしなに辞書にしてくれる。実行結果はこんな感じ
{ "created_at": "2012-02-08 00:06:12", "text": "entry text", "id": 1, "comments": [ { "created_at": "2012-02-08 00:06:12", "text": "comment1 text", "entry_id": 1, "id": 1 }, { "created_at": "2012-02-08 00:06:12", "text": "comment2 text", "entry_id": 1, "id": 2 } ] }
Mapperクラスを継承して、必要なフィールドを定義するだけで目的が達成できてしまった。RawFieldはオブジェクトが持つ同名のプロパティをそのままセットしてくれる。callbackも呼べるので適宜データを加工した上でセットすることも可能。
ポイント は ListDelegateField 。 リストの各要素に対するマッピング処理を別のMapperクラスに委譲できる。この例でいうと、commentsの各要素であるCommentオブジェクトのマッピング処理は、CommentMapperに任せてる。
まとめ
駆け足で説明したけど、まとめるとこんな感じ
- Mapperを定義するだけで、オブジェクト自身のデータをゴニョゴニョしなくていいのは大変嬉しい。
- DelegateFieldがある事で、ネストしたデータ構造でも安心して使える。
- Modelクラスと1 : 1 になるように掛けるので、ごちゃごちゃしない
以上おわり。
おまけ
コードの断片だと分かりにくいと思うので、サンプルコードをまるっと貼付けておく。
#!/usr/bin/env python #-*- coding:utf8 -*- import json from datetime import datetime from sqlalchemy import create_engine, Column, Integer, String, DateTime, ForeignKey from sqlalchemy.orm import scoped_session, sessionmaker, relationship from sqlalchemy.ext.declarative import declarative_base from bpmappers import Mapper, RawField, ListDelegateField # create base engine = create_engine('sqlite://', convert_unicode=True, echo=False) db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine)) Base = declarative_base() Base.query = db_session.query_property() # declare models class Entry(Base): __tablename__ = 'entries' id = Column(Integer, primary_key=True) text = Column(String) created_at = Column(DateTime, default=datetime.now, nullable=False) comments = relationship('Comment') class Comment(Base): __tablename__ = 'comments' id = Column(Integer, primary_key=True) entry_id = Column(Integer, ForeignKey('entries.id'), nullable=False) text = Column(String) created_at = Column(DateTime, default=datetime.now, nullable=False) # declare mappers class CommentMapper(Mapper): id = RawField() entry_id = RawField() text = RawField() created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S')) class EntryMapper(Mapper): id = RawField() text = RawField() created_at = RawField(callback=lambda x:x.strftime('%Y-%m-%d %H:%M:%S')) comments = ListDelegateField(CommentMapper) # insert data Base.metadata.drop_all(bind=engine) Base.metadata.create_all(bind=engine) db_session.add(Entry(text='entry text')) db_session.commit() db_session.add(Comment(entry_id=1, text='comment1 text')) db_session.add(Comment(entry_id=1, text='comment2 text')) db_session.commit() # select data print '-' * 10 entries = Entry.query.all() for entry in entries: print 'entry.id: ' + str(entry.id) print 'entry.text: ' + entry.text print 'entry.creted_at: ' + str(entry.created_at) for comment in entry.comments: print ' ' + ('-' * 9) print ' comment.id: ' + str(comment.id) print ' comment.text: ' + comment.text print ' comment.created_at: ' + str(comment.created_at) # convert to json print '-' * 10 print json.dumps(EntryMapper(entries).as_dict(), indent=2)
ディレクトリ内のファイルを作成時刻でソートして連番をつけてリネームする
という感じのブログを見つけたので自分だったらどうやるかなぁと思ってやってみる。
前提
とりあえず上のブログを見習って、なるべく限定的な条件にして、お手軽にくむ
- 再帰的にディレクトリを辿るとかいらない。
- リネーム対象はファイルのみ
- 拡張子はリネーム後でも変わらずに
- リネームする時のフォーマットは、決めうちで
- ソートは昇順のみで良い
- バックアップとかも考慮いれない
こうした
#!/usr/bin/env python # coding: utf-8 import os import sys import stat DIR_PATH = "/Users/hogehoge/hogehoge" RENAME_PATTERN = "IMG_%05d%s" if __name__ == "__main__": if not os.path.isdir(DIR_PATH): sys.exit("ERROR: '%s' is not directory." % DIR_PATH) index = 0 (root, dirs, files) = next(os.walk(DIR_PATH.rstrip(os.sep))) targets = dict([(os.stat(os.path.join(root, f)).st_ctime, f) for f in files]) for st_ctime, filename in sorted(targets.items()): index += 1 (name, ext) = os.path.splitext(filename) os.rename(os.path.join(root, filename), os.path.join(root, RENAME_PATTERN % (index, ext)))
余談
- ディレクトリセパレータは os.sepでとれる事をしった
- os.path.splitextも初めてしった。
追記(2012/02/05)
- 海原雄山なのか戦国大名なのか段々見分けるのが困難になってきた御館様(id:imagawa_yakata)に下記のようにTwitterで教えてもらいました。いつもありがとうございます。m( _ _ )m
- なるほど調べてみたら「PEP3114」として上がってるんですね。知らんかった。
- といわけで上記のコードを下記のように修正しました。
- (root, dirs, files) = os.walk(DIR_PATH.rstrip(os.sep)).next() + (root, dirs, files) = next(os.walk(DIR_PATH.rstrip(os.sep)))
利用可能なモジュール一覧を取得する
ふとPythonで利用可能なモジュールの一覧を取得したくなったのでメモ。
条件
ここでいう利用可能なモジュール および パッケージ一覧とは、以下のようなものとする。
- ビルトインで組み込まれてるやつ
- site-packages にインストールされてるやつ
- virtualenvなどの仮想環境などにインストールされてるやつ
- というかパスが通ってて、モジュール、パッケージとして認識されるヤツ全て
- 確認したいのじゃなくてリストとかのデータとして扱いたい
こんなん僕が求めてるのとちゃう
求めてるものとはなんか違った夢の残骸達
sys.builtin_module_names
文字通り、ビルトインで入ってるやつの一覧。足りない
import sys print sys.builtin_module_names #=> ('__builtin__', '__main__', '_ast', '_codecs', '_sre', '_symtable', '_warnings', '_weakref', 'errno', 'exceptions', 'gc', 'imp', 'marshal', 'posix', 'pwd', 'signal', 'sys', 'thread', 'xxsubtype', 'zipimport')
sys.modules
実行時にロード済みなやつの一覧。まだ足りない。とういかロードしてない組み込みのヤツは出てこない。
import sys print sys.modules.keys() #=>['cStringIO', 'copy_reg', 'encodings', 'site', '__builtin__', '__main__', 'encodings.encodings', 'abc', 'posixpath', 'flaskext', '_weakrefset', 'errno', 'pprint', 'encodings.codecs', '_abcoll', 'types', '_codecs', 'new', '_warnings', 'genericpath', 'stat', 'zipimport', 'encodings.__builtin__', 'warnings', 'UserDict', 'encodings.utf_8', 'sys', 'codecs', 'os.path', 'signal', 'linecache', 'posix', 'encodings.aliases', 'exceptions', 'os', '_weakref']
help('modules')
めっさ一杯出た。大分近い。仮想環境にいれたヤツとかもちゃんと拾ってくれてる。。。けど結果が標準出力されるのでなんか微妙
$ python -c "help('modules')" Please wait a moment while I gather a list of all available modules... ArgImagePlugin _Mlte difflib pimp Audio_mac _MozillaCookieJar dir_help pip BaseHTTPServer _OSA dircache pipes Bastion _Qd dis pkg_resources BdfFontFile _Qdoffs distutils pkgutil BeautifulSoup _Qt django_assets platform BeautifulSoupTests _Res doctest plistlib BmpImagePlugin _Scrap dprint popen2 ... ...
これや僕が求めてたのはこれなんや
「python -c "help('modules')"」の結果は、「pydoc modules」と等価である。というかhelpは多分pydocを見てる。というわけで、pydoc.pyをあら探しする。
であった。それが「pydoc.ModuleScanner」とかいうヤツ。pydoc.pyではこんな使いかたしてた。
1879 modules = {} 1880 def callback(path, modname, desc, modules=modules): 1881 if modname and modname[-9:] == '.__init__': 1882 modname = modname[:-9] + ' (package)' 1883 if find(modname, '.') < 0: 1884 modules[modname] = 1 1885 def onerror(modname): 1886 callback(None, modname, None) 1887 ModuleScanner().run(callback, onerror=onerror) 1888 self.list(modules.keys())
これを使って「pydoc modules」は実現されていた。単純に一覧が欲しいだけので、コピってこんな感じに変えてみた。
#!/usr/bin/env python # -*- coding: utf-8 -* from pydoc import ModuleScanner from string import find modules = [] def callback(path, modname, desc, modules=modules): if modname and modname[-9:] == '.__init__': modname = modname[:-9] if find(modname, '.') < 0 and modname not in modules: modules.append(modname) def onerror(modname): callback(None, modname, None) ModuleScanner().run(callback, onerror=onerror) print modules
これを実行すると。。。
$ python all_modules.py ['__builtin__', '_ast', '_codecs', '_sre', '_symtable', '_warnings', '_weakref', 'errno', 'exceptions', 'gc', 'imp', 'marshal', 'posix', 'pwd', 'signal', 'sys', 'thread', 'xxsubtype', 'zipimport', 'all_avaliable_modules', 'all_avaliable_modules2', 'ascii_to_ebcidic', 'blinker_sample', 'bool_judge', 'cast', 'class_name', 'converting_byte', 'datetime_test', 'dict_key_map', 'dict_merge', 'diff_time', 'dir_help', 'dprint', 'fib', 'file_abs_path', 'fold_test', 'func_args_disruptiv ... ...
おー。いっぱいでてきた。ビルトインも、仮想環境も、カレントディレクトリのヤツも全部出てきたw
サブモジュール、パッケージも一覧に含める
上のヤツでは、トップレベル(と呼んでいいんだろうか?)のパッケージとモジュールの一覧しか表示されないようになってる。サブモジュール・パッケージも含めて一覧で表示したかったら。「callback」関数をこう変えればいい。
def callback(path, modname, desc, modules=modules): if modname and modname[-9:] == '.__init__': modname = modname[:-9] - if find(modname, '.') < 0 and modname not in modules: - modules.append(modname) + if modname not in modules: + modules.append(modname)
愚行
ModuleScannerに辿り着く前は、もういいやって気分で「pydoc modules」の標準出力をパースして強引にリストを取得しようとしている愚かな僕がいました。けど「sys.path.insert」で追加したパスのモジュールとか取得できないじゃんとか思ってやめた
#!/usr/bin/env python # -*- coding: utf-8 -*- import pprint import commands import re ret = commands.getoutput('python -c "help(\'modules\')"') module_list = [] start = False for line in ret.split('\n'): if re.match(r'[^ ]+[ ]+[^ ]+[ ]+[^ ]+[ ]+[^ ]+$', line): start = True if start and line == '': break if not start: continue line = re.sub(r'[ ]+',r' ', line) for module in line.split(' '): if module.strip(' ') != '': module_list.append(module.strip(' ')) module_list.sort() pprint.pprint(module_list)
追記(2015/3/4)
コメント欄で 教えて貰った「pkgutil.iter_modules」を使うとインストール済みのやつとかを一覧でみることができるようです。いい感じですね。:id:hirokiky さんありがとうございます。
>>> import pkgutil >>> for m in pkgutil.iter_modules(): ... print m ... (<pkgutil.ImpImporter instance at 0x103103e18>, 'UserDict', False) (<pkgutil.ImpImporter instance at 0x103103e18>, '_abcoll', False) (<pkgutil.ImpImporter instance at 0x103103e18>, '_weakrefset', False) (<pkgutil.ImpImporter instance at 0x103103e18>, 'abc', False) (<pkgutil.ImpImporter instance at 0x103103e18>, 'codecs', False)
余談
- ModuleScannerのonerrorという関数は、何かしらのエラーでimportできないパッケージに対して呼び出される。(モジュールではなく)
Google Appengine で datastore_admin が使えなくなった
GoogleAppengine の Python SDK の バージョン1.6.2が出ましたね。早速upgradeしてみたら。下記のようなエラーに遭遇したのでメモ
datastore_admin が無いよとエラー
dev_appserver.py を叩いたら下記のようなエラーがでました。
google.appengine.ext.builtins.InvalidBuiltinName: datastore_admin is not the name of a valid builtin. Available handlers are: admin_redirect, appstats, default, deferred, django_wsgi, remote_api
どうやらビルトインの中から、datastore_adminは無くなったようです。もう少し正確に言うと、Python2.7 で開発してる場合のみdatastore_adminが使えないっぽいです(2.5で試してないけど) 。リリースノートにもそのように書いてありますた。
2.7で開発をしているというのは、app.yamlでruntimeを「python2.7」にしているという事です。
runtime: python27
というわけでapp.yamlから削ったった。
builtins: - datastore_admin: on #<- 削った
ASCII to EBCDIC(符号付き数値)
数年前、全銀協のフォーマットで且つ、EBCDICコードで相手側のシステムに送信しなければならないという案件と格闘していた。全銀協とは全国銀行協会の略で、全国の銀行は、ここが定めるプロトコルおよび、データフォーマットで振込情報のやりとりやらなんやらをしている。
ファームバンキングと全銀形式(全銀ファイルフォーマット) - Shoulder.jp
ASCIIコードからEBCDICコードへの変換
そもそも僕はウェッブケー一本でやってきたのでEBCDICコードをどうやってLinux上で生成するのかわからなかった。調べた結果、ddコマンドを使えば簡単にデータファイルの変換が出来る事がわかった。
dd if=./input.txt of=./output.txt conv=ebcdic
数値データはサイン(符号)付きで
ddコマンドで変換を掛ければそれで済むと思った。ところがEBCDICの数値データはサイン(符号)付きでなければ駄目と言われた。(振込情報には金額のデータがあったので)
「はぁ?」
EBCDICコードでは数値データを 表現する場合、最終桁の数値は8bitのウチ上位4ビットを符号を示すものにしなければならなかった。仕様書にはCOBOLで言う所のS9という型で表記されていた。
ddコマンドは単に文字列をEBCDICコードに変換するだけなので、符号付きの数値データを当然考慮してくれなくて超困った。
ASCIIコードの数値を強引に変換
- 最終桁の上位4bitが違うという事は = ASCIIコード上では最終桁が数値(0-9)では無くなる事を意味する。
- 正負によって上位4bitはの値は変わってくる。
以上の手がかりをもとに、バイナリエディタと、EBCDICコード表もとにEBCDICの符号付き数値がASCIIコードで表現された時には最終桁はどんな文字列になるのかを泣きながら調べた。
そして下記のような関数を書いた(当時はPHPで書いたけど)。そしてデータファイル(上の例で言えばinput.txt)を書き出す前に、数値データはこの関数を通して書き出すようにした。
def ascii2ebcdic_sign_number(number): plus_codes = {0:'{', 1:'A', 2:'B', 3:'C', 4:'D', 5:'E', 6:'F', 7:'G', 8:'H', 9:'I'} minus_codes = {0:'}', 1:'J', 2:'K', 3:'L', 4:'M', 5:'N', 6:'O', 7:'P', 8:'Q', 9:'R'} codes = minus_codes if number < 0 else plus_codes return str(abs(number))[:-1] + codes[abs(number) % 10] print ascii2ebcdic_sign_number(1001) #=> 100A print ascii2ebcdic_sign_number(-1001) #=> 100J
こうやって変換した結果をddコマンドで一発変換すれば、EBCDICコードでの数値データを表現できた。ブログに書くとさらっとだけど、この後にさらにベンダー出してる商用の暗号化ソフトとか、全銀クライアントとの格闘があって、終わった直後は放心というか憔悴してたと思う。