Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

xonshのCore Eventsまとめ

- xonshのCore Eventsとは -

XonshのCore Eventsは、xonshを自分で改修していく上で大事な「xonshの動作をtriggerとして発火するもの」です。

xonshの良いところは、EventsをPythonの関数のデコレータとして記述する事だけで「EventをHookして何かを実行する」という事が可能になる所です。


公式ドキュメントのsampleを例に見てみます。

# cdなど移動系コマンドのイベントをトリガーにadd_to_fileが起動する
@events.on_chdir
def add_to_file(olddir, newdir, **kw):
    with open(g`~/.dirhist`[0], 'a') as dh:
        print(newdir, file=dh)

上記コードでは、ディレクトリ移動が発生したタイミングで移動情報をファイルに追記するようになっています。
参考 : Tutorial: Events — xonsh 0.6.0.dev151 documentation


Core Eventsを把握しておくことは、Xonshの理解に繋がる訳です。


 

- Core Events -

以降はCore Eventsについて記述しておくものです。

主に以下に記載されたxonshに内装されているEventsについてです。
Core Events — xonsh 0.6.0.dev151 documentation

 

設定周り

rcファイル読み込み前

on_pre_rc() -> None
http://xon.sh/events.html#on-pre-rc-none
設定ファイルとなるxonshrcを読み込む前に発火するイベント
正確には以下の中で発火するので、起動時で言うとon_post_initの直前
https://github.com/xonsh/xonsh/blob/88647b4a8f6f2611dbb03370879221b1ca9fa89a/xonsh/main.py#L251

 

rcファイル読み込み後

on_post_rc() -> None
http://xon.sh/events.html#on-post-rc-none
設定ファイルとなるxonshrcを読み込んだ後に発火するイベント
正確には以下の中で発火するので、起動時で言うとon_post_initの直前
https://github.com/xonsh/xonsh/blob/88647b4a8f6f2611dbb03370879221b1ca9fa89a/xonsh/main.py#L251

 

初期化後

on_post_init() -> None
http://xon.sh/events.html#on-post-init-none
Xonshスクリプトや対話コンソール等のための初期化が済んだ後に発火するイベント
対話コンソールやスクリプト等関係なく発火する
この後対話コンソールとして起動した場合は、on_pre_cmdloopが発火する

 

cmdloopに入る時

on_pre_cmdloop() -> None
http://xon.sh/events.html#on-pre-cmdloop-none
xonshrcや各moduleのimportを終えて、対話コンソールが入力待ち状態に入る前に発火するイベント
正確な場所は以下
https://github.com/xonsh/xonsh/blob/88647b4a8f6f2611dbb03370879221b1ca9fa89a/xonsh/main.py#L347

 

cmdloopから抜けた時

on_post_cmdloop() -> None
http://xon.sh/events.html#on-post-cmdloop-none
xonshがコマンド入力やPythonスクリプトを受け続ける状態を抜けた時に発火するイベント

 

ptkを読み込んだ時

on_ptk_create(prompter: Prompter, history: PromptToolkitHistory, completer: PromptToolkitCompleter, bindings: KeyBindingManager)
http://xon.sh/events.html#on-ptk-create-prompter-prompter-history-prompttoolkithistory-completer-prompttoolkitcompleter-bindings-keybindingmanager
xonshが利用するptkを読み込んだ時に、ptkを渡してくれる
ptkを使ったキーバインドの設定等でよく使う
正確には以下がinitされた時に発火するイベント
https://github.com/xonsh/xonsh/blob/72f3bc0d089ea91d4e5288bb1c44ebfbe81db43e/xonsh/ptk/shell.py#L34

 

exitする前

 
on_exit() -> None
http://xon.sh/events.html#on-exit-none
正確にはmain_xonsh内のfinally内でこのイベント発火だけ発生して、その後はxonshが落ちて別のlogin shellが起動される
https://github.com/xonsh/xonsh/blob/88647b4a8f6f2611dbb03370879221b1ca9fa89a/xonsh/main.py#L347
このイベントを起点にExceptionを落とすとlogin shell出ない
上記on_post_cmdloopの直後に発火する形だが、違いは対話コンソールの時以外でもon_exitは発火する点である
スクリプトなどでもhookしたい場合に使う


 

moduleをインポートする時

インポートする前

on_import_pre_create_module(spec: ModuleSpec) -> None
http://xon.sh/events.html#on-import-pre-create-module-spec-modulespec-none
import hogeする前に発火するイベント
指定した”hoge”がspecとして渡される
 
on_import_pre_exec_module
http://xon.sh/events.html#on-import-pre-exec-module-module-module-none
loader.create_moduleではなくloader.exec_moduleによってimportされる前に発火するイベント
Pythonでは主にexec_moduleが使われていくようになる(Python 3.4でexec_moduleが追加され、3.6以降はメインで使われるように)
動作は上と同じ

インポートする後

 
on_import_post_create_module(module: Module, spec: ModuleSpec) -> None
http://xon.sh/events.html#on-import-post-create-module-module-module-spec-modulespec-none
import hogeした後に発火するイベント
create_moduleしたhogeの内容がmodule、hogeがspecとして渡される
 
on_import_post_exec_module
http://xon.sh/events.html#on-import-post-exec-module
loader.create_moduleではなくloader.exec_moduleによってimportされた後に発火するイベント

 

moduleを探す前

on_import_pre_find_spec(fullname: str, path: str, target: module or None) -> None
http://xon.sh/events.html#on-import-pre-find-spec-fullname-str-path-str-target-module-or-none-none
moduleを探すためのimportlib.util.find_specが利用される前に発火するイベント
以下find_specの引数に指定した名前やPathが渡される
https://docs.python.org/3.6/library/importlib.html#importlib.util.find_spec

 

moduleを探した後

on_import_post_find_spec(spec, fullname, path, target) -> None
http://xon.sh/events.html#on-import-post-find-spec-spec-fullname-path-target-none
moduleを探すためのimportlib.util.find_specが利用された後に発火するイベント
module本体や名前、Pathなどが渡される

 

環境変数を触った時

新規環境変数作成時

on_envvar_new(name: str, value: Any) -> None
http://xon.sh/events.html#on-envvar-new-name-str-value-any-none
環境変数作成時に発火するイベント
新しい変数名と値が渡される
on_envvar_new内で環境変数を変更すると、変数アプデ -> on_envvar_new -> 変数アプデ -> on_envvar_new -> … とループに陥るので注意
 
 

環境変数アップデート時

on_envvar_change(name: str, oldvalue: Any, newvalue: Any) -> None
http://xon.sh/events.html#on-envvar-change-name-str-oldvalue-any-newvalue-any-none
環境変数アップデート時に発火するイベント
変数名と前の値と新しい値が渡される
on_envvar_newと同じくループに注意

 

コマンド実行時

ディレクトリ移動時

on_chdir(olddir: str, newdir: str) -> None
http://xon.sh/events.html#on-chdir-olddir-str-newdir-str-none
cdコマンド等でディレクトリが移動した際に発火するイベント
ディレクトリの移動先と移動元が渡される

   

コマンド実行(コマンド変換時)

on_transform_command(cmd: str) -> str
http://xon.sh/events.html#on-transform-command-cmd-str-str
コマンドを実行する前に発火するイベント
以下で発火するため、複数行の場合繰り返し実行されたりする
https://github.com/xonsh/xonsh/blob/f05c4d86b8529703832c2f0bb5fe2790dd3c5b66/xonsh/shell.py#L60
正確には以下のpushメソッドで「compileされる時」であり、実行時ではない。
https://github.com/xonsh/xonsh/blob/72f3bc0d089ea91d4e5288bb1c44ebfbe81db43e/xonsh/ptk/shell.py#L123

 

コマンド実行前

on_precommand(cmd: str) -> None
http://xon.sh/events.html#on-precommand-cmd-str-none
コマンド実行前に発火するイベント
入力された文字列が渡される

 

コマンド実行後

on_postcommand(cmd: str, rtn: int, out: str or None, ts: list) -> None
http://xon.sh/events.html#on-postcommand-cmd-str-rtn-int-out-str-or-none-ts-list-none
コマンド実行後に発火するイベント
コマンド名やioが渡される

 

一行処理前

on_pre_prompt() -> None
http://xon.sh/events.html#on-pre-prompt
(ドキュメントにはon_first_promptとなっているがミスか)
対話コンソールで一行を読み込んで処理する前に発火するイベント
正確な場所は以下で、入力をhistoryに追加した後である
https://github.com/xonsh/xonsh/blob/5cb3e8dd5545a7448b2a393379e3405c1942d1e0/xonsh/readline_shell.py#L276

 

一行処理後

on_post_prompt() -> None
http://xon.sh/events.html#on-post-prompt
対話コンソールで一行を読み込んで処理した後に発火するイベント


  

- おわりに -

Core Eventsは大事なEventで実装されているイベント以外にも、自前でEventを継承してXonsh内にOverrideさせる事もできるので、xonshの動作をかなり拡張することが可能である。

また、逐一追っていくとXonshの動作のデバッグにもなるので、一度触ってみると吉。

 
アドベントカレンダーまだ空いてるから頼むという所で記事はおしまいです。

qiita.com