iTerm2 Version 3.3 に実装されたPython scripting API チュートリアル


イントロ

A Python scripting API has been added to enable
extensive configuration and customization.

https://iterm2.com/downloads/stable/iTerm2-3_3_0.changelog

というわけでiTerm2のversion3.3からPythonでiTerm2の通知・タブ・ウィンドウといった各種操作のAPIを叩けるようになりました。

早速exampleを混ぜつつ解説記事です

pythonの基礎的な文法の記述ができることを前提知識としています。

下準備

兎にも角にもiTerm2のversionを3.3にしてください。

間違ってスキップしてしまったりアップデートの通知が来ないという人は上のメニューバーから iTerm > Check For Updates... でアップデートのチェックが行なえます。

バージョンアップしたらハイ終わり。というわけではなくもうひと手間、 Script > Manage > New Python を選択し、 Basic > Simple をクリックしてください。(デーモン化など選択できますが今は一旦置いておきましょう

さてこれで準備は終わりです。

exmaple.py

上記の準備が終わったら Script の中に exmaple.py というのが増えてるのが確認できます。
(もし出てこない無い人がいたら後述のコードを指定の場所に置いてください

試しに押してみましょう。

新しいtabが開いたと思います。

まずはこれを解きほぐして行きましょう

Scriptの置く場所

まず、example.pyは一体どこにいるのか。

答えは ~/Library/ApplicationSupport/iTerm2/Scripts/ 配下にいます。

iTerm2 v3.3.0以降は ~/Library/ApplicationSupport/iTerm2/Scripts/ 配下の末尾が .py で始まるものを全てscriptと認識するようになりました。( `~/Library/ApplicationSupport/iTerm2/Scripts/*.py)

試しに touch ~/Library/ApplicationSupport/iTerm2/Scripts/hoge.py と空のファイルを作成して新しいtabを開いたりすると Script の中に hoge.py が増えていることが確認できると思います。

読み方

次に実際に中身を読んで行きましょう

以下がコードです。準備時に作成されていない人がいたらこれをそのままコピペして ~/Library/ApplicationSupport/iTerm2/Scripts/exmaple.py に置いてください。

#!/usr/bin/env python3

import iterm2

async def main(connection):
    app = await iterm2.async_get_app(connection)
    window = app.current_terminal_window
    if window is not None:
        await window.async_create_tab()
    else:
        print("No current window")

iterm2.run_until_complete(main)

各セクションごとに解説していきます

#!/usr/bin/env python3

import iterm2

ここはすっ飛ばします

async def main(connection):

async はpython書いてる人でも見かけない人も多々いると思うので、軽く説明すると非同期処理を行うときのお作法でasyncをつけた関数をrun_until_complete()で呼び出すと非同期処理が行なえます。

詳しく知りたい方はこちらの記事を推奨します。

Pythonにおける非同期処理: asyncio逆引きリファレンス - Qiita

なぜiTerm2のAPI呼び出しに非同期処理が必要なのかというと、iTerm2のAPI連携はwebsocketを介して行われ、その際に双方の通信に数ミリ秒待機する必要があるからです。
非同期処理を挟むことでこの待機時間に他の処理を挟むことができたり、適切なエラーハンドリングが可能になります。

なので今は特に難しく考えずにとりあえず非同期処理にしておけば後々楽になる。ということぐらいを覚えておきましょう。

app = await iterm2.async_get_app(connection)

iTerm2のAPIの殆どはawaitで呼び出さなくてはいけないことに留意しましょう。
このAPIはiTerm2.Appオブジェクトの取得を行っています。

window = app.current_terminal_window

このAPIは上記で取得されたiTerm2.Appオブジェクトから「現在作業中のターミナルウィンドウ」を取得します。ここでいう「現在作業中のターミナルウィンドウ」はiTerm2がアクティブ時に入力受付状態になっているウィンドウを指します。

    if window is not None:
        await window.async_create_tab()
    else:
        print("No current window")

見ての通りのif文ですが、上記で取得されたwindowが存在した場合は window.async_create_tab() がコールされて新しくtabが生成されます。

sort.py

もう一例としてタブをsortするexample scriptを見てみましょう

#!/usr/bin/env python3

import asyncio
import iterm2
import time

async def main(connection):
    app = await iterm2.async_get_app(connection)
    for window in app.terminal_windows:
        tabs = window.tabs
        for tab in tabs:
            tab.tab_name = await tab.async_get_variable("currentSession.name")
        def tab_name(tab):
            return tab.tab_name
        sorted_tabs = sorted(tabs, key=tab_name)
        await window.async_set_tabs(sorted_tabs)

iterm2.run_until_complete(main)

読み方

#!/usr/bin/env python3

import asyncio
import iterm2
import time

相も変わらずここは省略します

async def main(connection):
    app = await iterm2.async_get_app(connection)

ここも先程と同じなので省略します

for window in app.terminal_windows:

app.terminal_windows とは現在開いているtabのリストが返り値が返ってくためそれをfor文で回します

       tabs = window.tabs
       for tab in tabs:
        tab.tab_name = await tab.async_get_variable("currentSession.name")
        def tab_name(tab):
            return tab.tab_name

ここでは tab.async_get_variable("currentSession.name") で現在のタブの情報として受け取り tab 内にあるtab_nameに引数で受け渡し、返り値で代入していきます

sorted_tabs = sorted(tabs, key=tab_name)

そしてタブの名前をsortedで新しいlistを作成します。(srot()とsrot()の違いはこちらを参照

await window.async_set_tabs(sorted_tabs)

window.async_set_tabs で先程sortされたtabをそのwindowにlist順にセットしていきます。

実際に動かした場合はこうなります

リファレンス

APIに関するリファレンスはこちらになります。

Python API — iTerm2 Python API 0.26 documentation