ホーム > タグ > Python

Python

Hack #136: Pythonインタフェースを使う(2)

前回はPythonインタフェースの基本を紹介しましたが、今回はもう少し突っ込んだ内容について紹介しようかと思います。

変数のコンテキスト

VimScriptからPythonを起動するとき、Pythonは呼ばれたVimScriptのコンテキストで実行されます。なので、以下の例のようにしてVimScriptとPython間で変数をやりとりすることができます。

" VimScript
let s:hoge = "s:hoge"

function Hoge(hoge)
    let hoge = "l:hoge"
    python <<EOM
print vim.eval('s:hoge') # s:hogeと表示
print vim.eval('a:hoge') # a:hogeと表示
print vim.eval('hoge')   # l:hogeと表示

vim.command('let s:hoge = "s:fuga"')
vim.command('let hoge = "l:fuga"')
EOM
    return hoge
endfunction

echo Hoge('a:hoge') " l:fugaと表示
echo s:hoge         " s:fugaと表示

注意点としては、前回vim.eval()のところでも述べたようにVimScriptの内容は全て文字列になるので数値で欲しい場合は、Python側でint(vim.eval("line('.')"))などとキャストする必要があるところです。他にもPythonにはboolean型がありますがVimScriptにはないなど微妙に落とし穴があるので、Python側のif文などでVimScriptの値を参照する際は気を付ける必要があります。また、:pyfileで読み込んだ外部ファイルの中でもこのコンテキストの扱いは有効です。例えば以下の通りです。

" VimScript
pyfile hoge.py
let s:fuga = 's:fuga'
python hoge("hoge")
" hoge.pyとする
def hoge(str):
    print vim.eval('s:fuga') + str # s:fugahogeと表示

また:pythonコマンドで生成したPythonの変数は全てグローバル変数となり、Vimの起動から終了まで全て同じコンテキストとなります。

" VimScript
python <<EOM
hoge = "hoge"
EOM

python <<EOM
print hoge # hogeと表示される
EOM

ユニコード文字の扱い

Pythonはスクリプト自体のエンコードを指定する方法としてマジックコメントを使うことができます。これは、スクリプトファイルの1行目か2行目に、coding:utf-8coding=utf-8などのように/coding[:=]文字コード名/で表される表現があればエンコードとして認識されるというものです。PEP 0263には.pyファイルをVimで編集する場合は、Vimのモードラインとしても有効になるように以下のように書く例が掲載されています。

# vim:set fileencoding=utf-8:

しかしモードラインで'fileencoding'は指定すべきでないので単に、

# coding=utf-8

などと書くべきです。VimScript内の:pythonコマンドではヒアドキュメント風の書き方を使った場合にだけこのマジックコメントを使うことができます。また、マジックコメントは各コマンドごとに書く必要があります。

" utf-8で保存されたVimScript、'encoding'がutf-8の環境
python <<EOM
print u"あいうえお".encode('utf-8') # スクリプトがasciiで書かれていると解釈され文字化けする
EOM

python <<EOM
# coding=utf-8
print u"あいうえお".encode('utf-8') # スクリプトがutf-8で書かれていると解釈されて文字化けしない
EOM

python <<EOM
print u"あいうえお".encode('utf-8') # 前のブロックのマジックコメントは反映されず文字化けする
EOM

ということなので、マルチバイト文字をやりとりする必要がある場合は基本的に:pythonコマンドで一行でスクリプトを渡す形式を使うことはできず、ヒアドキュメント風の使い方をすることになります。また、より汎用的にするには以下のように書くと良いでしょう。

" utf-8で保存されたVimScript、'encoding'は不明
python <<EOM
# coding=utf-8
VIM_ENCODING = vim.eval('&encoding')
print u"あいうえお".encode(VIM_ENCODING)
EOM

Vimから入力を受け取ってPythonに渡すときも同様にしてdecodeすれば文字化けせずにユニコード文字列を渡すことができます。

Pythonのimport

例えば、Pythonインタフェースを作ったプラグインを作っていて、Python部分が長くなるので外部に.pyファイルとして出しておきたい場合を考えます。そんなときは、通常は:pyfileを使えば良いのですが、これはグローバル空間でファイルの中身を実行するので、from module import *したときと同様に外部ファイルの関数や変数を全てグローバル空間に展開してしまいます。これを避けるための方法を考えてみます。

 " pytest.py
def func():
    print "hoge"
 " VimScript (:pyfileを使った例)
pyfile pytest.py
python <<EOM
func() # hogeと表示される
EOM
 " VimScript(:pyfileを使わない例)
let path = expand('%:p:h')
python <<EOM
import vim, sys
sys.path.append(vim.eval('path'))

import pytest # この.vimファイルと同じディレクトリにpytest.pyを置いておく
pytest.func() # hogeと表示される
EOM

このように、VimScript側でスクリプトの置いてあるディレクトリを取得し、sys.pathにパスを追加しておくことで、所望の.pyファイルをimportすることができます。

今回は以上です。前回と今回で説明した内容を理解すれば、Pythonインタフェースを利用したpluginも自在に書けるようになると思います。

tsukkee

Hack #132: Pythonインタフェースを使う(1)

VimにはRubyやPython、Perl、SchemeなどのVimScript以外の言語のインタフェースがあります.これらの言語にはVimScriptにはない強力なライブラリを持っているものも多く、ネットワーク通信やファイルシステムの操作など、VimScript単体では難しい処理を行う際に便利です。今回はその中でPythonインタフェースの使い方について簡単に解説したいと思います。

そもそもPythonインタフェースを使うには?

Vimを --enable-pythoninterp 付きでconfigureしてビルドする!だけではあんまりなので、それ以外の方法を一部紹介します。

  • Mac
    • MacVim-KaoriYaにはデフォルトでPythonインタフェースが組み込まれているので、これを使うのが一番簡単です。
    • MacPortsで+python(または+python26)でインストールするのでもOKです。
  • Windows
    • KaoriYa VimPythonのDLLが存在するとPythonインタフェースが使えます。詳しくは:help python-dynamicを参照してください。
  • Linuxなど
    • UbuntuとDebianではvim-pythonパッケージでPythonインタフェースを有効にできるそうです
    • 残念ながら筆者はその他の環境で簡単にPythonインタフェースを使う方法を知りません。Linuxの各ディストリビューションに含まれているVimの状況などご存知の方は、コメント欄などでお知らせいただけると幸いです。

Pythonインタフェースが使えるか確認する

以下のコマンドで1が返ればOKです。

:echo has('python')

VimScriptからPythonを呼ぶ

:pythonコマンドを使います。

" VimScript
:python print "hoge"; print "hoge"

:python <<EOM
print "fuga"
print "fuga"
EOM

といった感じで,そのまま引数にPythonスクリプトを渡すか,複数行のスクリプトを渡すときは2番目のようにヒアドキュメント風にすることでPythonを実行できます。Pythonはインデントが重要なので2番目の呼び方を用いるときには注意が必要です。ちなみに、print文などPythonからの標準出力は:echomsgで出力するのと同じ扱いで、エラーは:echoerrしたのと同じ扱いになります。また、input()やraw_input()などの標準入力からのインプットはサポートされていません。

" VimScript
function Hoge()
    python <<EOM
    print "hoge"
    EOM
enfcuntion " エラー!

上記のようについつい書きたくなりますが、これはエラーになります。Pythonは必ずインデントなしの状態で書き始めないといけませんし、EOMの前に空白があるとPythonブロックの終了と見なされません。正しくは、以下のように書きます。

" VimScript
function Fuga()
    python <<EOM
print "fuga"
EOM
endfunction

そのほか、Pythonの書かれたファイルを読み込んで実行する:pyfileコマンドもあります。

PythonからVimScriptを呼ぶ

Pythonからはvimモジュールを利用することで、VimScriptを呼ぶことができます。importしないと使えないので注意してください。

# Python
import vim

vimモジュールにはvim.command()とvim.eval()の二つの関数が用意されています。vim.command()は引数に渡されたVimのコマンドを実行します。例えば以下のように使います。

# Python
vim.command('echo "hoge"')
vim.command('let val = "fuga"')

vim.eval()は与えられたVimの式を評価して返します。配列もちゃんとPythonの配列にして返してくれます。ただし、数値であっても全て文字列として返されるので注意してください。例えば以下のような感じになります。

# Python
color_name = vim.eval('g:color_name')
bufnum, lnum, col, off = vim.eval('getpos(".")') # 例えば、("0", "3", "10", "0")のようになる

また,Vimのバッファを表すオブジェクトとしてvim.bufferが提供されています。vim.buffersが全てのバッファが入った配列で、vim.current.bufferが現在表示しているバッファになります。:help pythonにもありますが、おおよその使い方は以下の通りです。

# Python
import vim
b = vim.current.buffer # 現在のバッファ
print b.name           # バッファ名
print b.number         # bufnr()で得られる値,vim.buffersのインデックスはbufnr()とは異なるので注意
b[0] = "hoge"          # 1行目を変更する
b[:] = None            # バッファを空にする
b = vim.buffers[1]     # 1番目のバッファ(bufnr()が1というわけではない)
del b[:]               # これもバッファを空にする
b[0:0] = ["fuga"]      # 一番上に行を挿入
del b[2]               # 3行目を削除
b.append("hoge")       # 末尾に行を追加 
print len(b)           # バッファの行数を表示

vim.buffersで全てのバッファを得られることからわかるように、VimScript単体では難しい表示していない(アクティブでない)バッファの内容を取得したり、その中身を編集したりすることができます。また、vim.bufferを用いた編集もVimの'modifiable'などの影響を受けるので、set nomodifiableされたバッファに書き込もうとすると、vim.error例外が発生し編集できません。

その他vim.window,vim.rangeなどが提供されていますが、今回はこれらの詳細は省略します。詳しくは:help pythonを参照してください。

今回は主に:help pythonにも書かれている内容について説明しましたが、次の「Pythonインタフェースを使う」では、もう少し突っ込んだ内容について説明したいと思います。

tsukkee

Home > Tags > Python

Search
Feeds
Links
  • 公式
  • 勉強会
  • 情報
  • コミュニティ
  • Meta
    Etc
    Creative Commons License
    This blog is licensed under a Creative Commons License.

    Return to page top