SyntaxHighlighter

2017年10月1日日曜日

NYAGOS を Windows で tcsh 風に使う

NYAGOS を Windows で tcsh 風に使う

私はこれまで Windows 上で tcsh をシェルとして使っていました。しかし、 RS2 にアップデートしたあたりから、なぜかクラッシュすることが多くなってきたので、代替のシェルを探していました。
パスを Windows 風にも使えることと、慣れてしまった tcsh 風のキーバインドをサポートしていることを条件に、いろいろなシェルを探してみました。

その結果、 tcsh から NYAGOS に変更してみることにしました。
ここでは、 tcsh に似せるために標準の NYAGOS に追加した、いくつかの変更についてまとめます。

NYAGOS とは

NYAGOS は zetamatta さんによって Windows 向けに開発されているシェルです。
本体は Golang で書かれており、 Lua スクリプトで拡張できるように設計されています。
また、日本語にもしっかりと対応されています。

素晴らしいシェルを公開していただき、ありがとうございます。

Windows で使えるシェルとしては、NYAGOS 以外にもたくさんのものがありますが、先に挙げた条件に加え、「Cygwin などの、 Windows ネイティブな環境でないものは使いたくない」、「日本語にきちんと対応しているものがよい」、「Golang を勉強したい」という理由により、 NYAGOS を使おうと思いました。

使い方

必要なファイル類は、 GitHub の私の Fork の tcsh_style のブランチにあります。

公式のリリースに比べ、上記のブランチでは nyagos.d/catalog の下の以下のファイルが追加されています。

case_sensitive_completion.lua

大文字と小文字を区別して補完するためのスクリプトです。

dollar_env.lua

環境変数を $TEMP 形式で (ある程度) 使えるようにするためのスクリプトです。
また、 echo $TEM[Tab] のような場合に、$TEMP と補完もしてくれます。
ただし、 $TEMP/[Tab] で Temp ディレクトリ以下のファイルを補完してくれたりはしません。

emphasize_completion.lua

補完候補の最初の一文字を強調表示するスクリプトです。
例えば、フォルダに fileA.txt, fileB.txt, fileC.txt がある場合、 ls file[Tab] と入力すると、 fileA.txt, fileB.txt, fileC.txt のように、次に入力するべき文字が強調されて表示されます。

tcsh_like_keybind.lua

tcsh の下記のキーバインドを実現します。

Alt+/
現在入力中の単語を、過去の入力データをもとに補完します。
Alt+p, Alt+n
現在入力中のコマンドを、過去の入力コマンドをもとに補完します。
Alt+BackSpace
カーソルより左の単語を一つ消去します。
Alt+d
カーソルより右の単語を一つ消去します。

なお、完全に tcsh と同一な動きをするわけではありませんし、それを目指しているわけでもありません。
日本語には対応しているつもりです。

.nyagos ファイルへの変更

上記の変更を有効にするために、 .nyagos ファイルに下記のような変更をします。

上記のように、末尾で use により先ほどのファイルを読み込みます。これで、それぞれの機能が有効になります。
なお、 dollar_env に関しては、 completion_hook の対応がやや古い状態なので、他のファイルより先に読み込んでおくとよいです。
また、 tcsh_like_keybind に関しては、内部で nyagos.prompt の書き換えを行っているので、 nyagos.prompt よりも後で読み込む必要があります。

これらの変更により、快適に Windows 上で tcsh ライクなシェル操作を実現できました。

2017年8月16日水曜日

Microsoft Edge の Native Messaging を使った拡張機能 その1 (UWP を用いた Native Messaging 拡張機能の概要)

Microsoft Edge の Native Messaging を使った拡張機能 その1 (UWP を用いた Native Messaging 拡張機能の概要)

Firefox, Chrome に続いて、 "Open TortoiseSVN" を Microsoft Edge にも移植してみたので、そのまとめです。

もう少し記事を分けるかもしれませんが、以下のような順番で書いていこうと思います。

  • その1: UWP を用いた Native Messaging 拡張機能の概要
  • その2: パッケージングの方法と、 Win32 アプリケーションの起動

なお、最終的なソースコードは GitHubの Open TortoiseSVN for Microsoft Edge にあります。

Edge の拡張機能のサポート

元々 Google Chrome の拡張機能で導入された拡張機能の形式ですが、 Mozilla Firefox にも WebExtensions として導入され、 Edge でも同様の機能がサポートされ始めていました。

具体的には、2016年の Anniversary update 以降、 Microsoft Edge も Chrome や Firefox とほぼ同様の拡張機能をサポートしています。
そして、 2017年の Creators update で、 Native Messaging のサポートも開始されました。

詳しい内容は Native messaging in Microsoft Edge のページに載っているので、基本的な作成方法などはこちらを参照するのがよいでしょう。

この記事では実際に上記のページに従って拡張機能を作成した際に、気になったところを中心に書こうと思います。

また、 Microsoft によるサンプルは SecureInput として GitHub で公開されています。

Firefox, Chrome との違い

Edge での Native Messaging サポートは、 Firefox, Chrome とは大きく違います。

Native messaging in Microsoft Edge のページに書かれているように、下記が特徴的です。

  • JavaScript で書かれた Extension は、 UWP 形式のアプリケーションとのみ通信できる。 Win32 形式のアプリケーションとは通信できない。
    Firefox, Chrome の場合は、 (Windows であれば) Win32 形式のアプリケーションと通信できる。
  • UWP アプリケーションとは、 AppService の仕組みを用いて通信を行う。
    Firefox, Chrome の場合は、標準入出力で JSON 形式のデータを所定のフォーマットでやり取りすることで通信を行う。

つまり、外部アプリケーションと通信する拡張機能の JavaScript 側のインターフェースは Firefox, Chrome と (ほぼ) 同じですが、それを受ける側であるアプリケーションの仕様は、まったく異なることになります。

なお、 Edge の Native Messaging 拡張機能の特徴として二つ書きましたが、実際には「外部のアプリケーションとは AppService の仕組みで通信を行う」という部分が重要です。
これは、 AppService は UWP アプリでしか作成できないので、必然的に UWP 形式のアプリケーションが必要条件となるためです。

UWP アプリとの通信

作成する AppService は、通常のものとまったく同じものです。なので、 UWP アプリで AppService を作成する方法については、他のページを探してみてください。
ここでは、 Edge との通信部分について書きます。

例として、仮に、 com.example.uwp という名前の AppService を作成するとします。

JavaScript 側のコード

その場合、 JavaScript 側から UWP アプリを呼び出すのは以下のようになります。

このように、 browser.runtime.sendNativeMessage もしくは browser.runtime.connectNative の第一引数で指定する application の名前は、 AppService の名前そのものになります。
それ以外は、 Firefox, Chrome の場合と何も違いはありません。

UWP 側のコード

AppService を実装する方法としては、 in-process な実装と、 out-of-process な実装の二種類があります。
詳しくは Microsoft の説明などを見るとよいでしょう。

Open TortoiseSVN では in-process な方法で実装したので、下記を実装する必要があります。

OnBackgroundActivated
他のアプリケーション (今回の場合は Edge) から AppService への接続があった場合、この関数が呼ばれます。
通常の AppService と同じように、この中で Connection に対してイベントハンドラを登録します。
OnAppServiceRequestReceived
他のアプリケーション (今回の場合は Edge) からメッセージが送られてきた際にそのメッセージを処理するためのイベントハンドラです。
Native Messaging のメッセージ処理の手順に合わせて実装します。

OnBackgroundActivated

OnBackgroundActivated の概要としては下記のような実装になりました。

こちらに関しては、特に通常の AppService と違いはないはずです。

OnAppServiceRequestReceived

この関数では、 Edge からのメッセージを受け取り、それに応じて処理を行います。

概要としては、以下のようなことをすればよいです。
ここでは、上で書いた例のように JavaScript 側から {"action": "action1", "args": ["arg1", "arg2", "arg3"]} という引数で呼ぶことを考えてみます。

いくつか重要な点を挙げておきます。

Edge からのメッセージは ValueSet の一つ目の Value として渡される。

ValueSet は辞書のように Key, Value の組を保存できるものですが、 AppService ではこの ValueSet を用いてデータをやり取りすることになっています。そのため、 Edge の Native Messaging 拡張機能も、 ValueSet でデータをやり取りすることになります。

Edge から送られてきたデータは、 JSON 形式の文字列となって、渡された ValueSet オブジェクトの最初の (唯一の) 要素の Value に格納されてきます。

Key は "Message" になっているようですが、これを仮定しないのがよいと思います。

UWP アプリからのメッセージも同様に ValueSet として返す。

逆方向もまったく同じです。
Key は適当な名前でよいので、 Value に JSON 文字列を指定し、 SendResponseAsync でメッセージを送ります。

JSON 形式にシリアライズするためには JavaScriptSerializer と匿名型を用いるのが楽でよいと思います。

なお、本筋とは関係ないですが、やや複雑な JSON 文字列をパースする場合、以下のようにすればよいです。
例えば、 {"action": "action1", "args": ["arg1", "arg2", "arg3"]}args をパースする場合は、例えば以下のようにすればよいです。

このようにすることで、 "args" の中身を配列で取り出すことができます。

あとは UWP アプリ内で好きな処理をすればよいわけです。

注意点

Microsoft EdgeHTML 15.15063 の時点では、 UWP アプリからの戻り値が (私の予想と) やや異なった形式で返ってきます。

具体的には、下記のようになります。

このように、 Firefox, Chrome と違ってわざわざ JSON.parse する必要があります。

私はこの挙動はバグのように思いましたので、 issue として登録しましたが、まだ有効な回答はない状態です。

続いて、作成した UWP アプリを JavaScript のコードとまとめてパッケージングし、 Edge の拡張機能として登録することになります。

次回以降は、下記について書く予定です。

  • Appx パッケージの作成
  • Win32 アプリケーションの起動
  • browser.runtime.sendNativeMessagebrowser.runtime.connectNative における注意点

2017年4月23日日曜日

Pausable Unittest その2 (開発の経緯)

Pausable Unittest その2 (開発の経緯)

概要

今回は、なぜ Pausable Unittest を Stackless Python のライブラリとして作成したかについて書いておこうと思います。

なお、ここで言及している Python は、基本的には Python 2.7 での話です。
最新の Python 3.5, 3.6 系列ではやや異なっているかもしれません。

Pausable Unittest 実現に必要な機能

Pausable Unittest では、以下のようなスクリプトを書くことを目的としています。

この中で重要なのは、(*1) の部分で Python インタプリタを終了し、システムを再起動した後で、続きを実行できるところです。

これを実現するためには、「関数の実行の中断と、その状態の保存/復元」が必要になります。
self.reboot() では、実行中の関数 test_check_reg を中断し、そのローカル変数の状態やどこまで実行したかの状態を含めて保存し、再起動後に復元する必要があります。

generator, fiber, coroutine, continuation

そこで、この機能を実現するために必要な要素技術は何か、について、検討をしました。
その結果、ジェネレータファイバーコルーチン継続などを、ファイルに保存し、あとで復元できればよいという考えに至りました。

ジェネレータ、ファイバー、コルーチン、継続は、いずれも「関数のようなものを途中で中断し、他に制御を移すことができる」という機能を持っています。

例えば、 JavaScript で書いたジェネレータでは、下記のようになります。

上記の例では、1, 2, 3, ..., 5 の順で数字が出力されます。
つまり、g.next() でジェネレータ gen に制御を移し、 yield で制御を戻す、といった流れになります。

これは、関数のような genyield によって中断できていることになります。

なお、 JavaScript ではジェネレータを定義する際には通常の関数と異なり function* を用いますが、 Python では「関数の中に yield があればジェネレータ」となっています。

参考までに Ruby のファイバーは下記のようになります。

こちらも同様に、1, 2, 3, ..., 5 の順で数字が出力されます。

このように、ジェネレータやファイバー (やコルーチン) には、「処理を中断し、処理を他に移すことができる」という特徴があります。

あとは、途中まで実行したジェネレータやファイバーを、ファイルに保存できればよいことになります。
先に挙げた Ruby の例では、下記のようなことができるとよいわけです。

しかし、これは Marshal.dump に失敗し、例外が送出され、うまく動作しません。

通常の Python でジェネレータを使って同様なことを書くと、やはりジェネレータを pickle できず、同様にうまく動作しません。

Stackless Python を選んだ経緯

通常の Python や Ruby で実現できない理由

通常の Python や Ruby で実現できないのは、もちろん「ジェネレータやファイバーをファイルに保存できないから」です。
これは、ただ未実装なわけではなく、技術的に実装が困難だからです。

Python や Ruby でのメソッドの呼び出され方

通常の Python や Ruby では、メソッドの呼び出し時にスタックが消費されます。
例えば、下記のような Python のスクリプトを考えます。

この関数 foo は、バイトコンパイルの結果、以下のようなバイトコードに変換されます。

Python は、与えられたソースコードを上記のようなバイトコードの変換した後、実際に処理を行います。
このとき、各行の命令を実行していくわけですが、ここで関数呼び出しの OpCode である CALL_FUNCTION がどのように処理されるかに注目してみます。

Python での CALL_FUNCTION の処理

現在の Python や Ruby では、ソースコードをバイトコードにコンパイルした後で、それを仮想マシン上で実行するようになっています。
Python の場合、上記のようなバイトコードに変換した後で、実行していきます。

「関数の呼び出し」の場合、通常は CALL_FUNCTION という OpCode に変換されます。
その後、実行時に ceval.cPyEval_EvalFrameEx 関数の中の switch 文で解釈されて実行されます。
Python 2.7.10 のコードを一部だけ抜き出すと、下記のようになっています。

このように、関数の実行そのものは call_function に委ねられ、処理が続けられます。

call_function では、いくつかの条件によって処理が分岐していきますが、間接的に PyEval_EvalFrameEx 関数が呼ばれます。
たとえば、ある条件下では fast_function が呼ばれますが、その中を追っていくと、条件に応じて PyEval_EvalFrameEx が呼ばれるのが分かります。

つまり、 Python インタプリタでは、 Python の関数呼び出しを Python 仮想マシンが実行する際に、 PyEval_EvalFrameEx の再帰呼び出しが発生し、その結果、スタックが消費されていることが分かります。

スタックが消費されることによる影響

スタックが消費される実装になっている場合、関数の状態を復帰させるのが非常に困難になります。

C 言語でのスタックの使い方は完全にコンパイラに依存しています。
例えば、スタック上には種々のローカル変数が配置されますが、それらをダンプしたり、復旧させたりする手段は、 C 言語には提供されていません。
もちろん、環境や OS 依存な方法を使ってスタックをダンプすることはできますが、ダンプしたスタックをそのまま復旧しても、状態が正しく復元されるわけではありません。
スタック上には種々の変数のアドレスなどがある場合もありますが、スタックがダンプされたときと復旧させた時で同じアドレスが使われるとは限らないからです。

そのため、スタックがある程度消費された状態を復元するのは、困難なものになります。

Stackless Python での CALL_FUNCTION の処理

一方で、 Stackless Python では、以下のようにやや処理が異なっています。

このように、 STACKLESS マクロに応じて、goto によるジャンプなどがはさまっています。

Stackless Python では、 call_function の中では、 Python の関数呼び出しは行わず、すべての処理が PyEval_EvalFrameEx のループに戻ってから行われるように工夫されています。
俗に「トランポリン呼び出し」と呼ばれるような方法に近い実装がされています。

その結果、 Stackless Python では、(一部の例外はありますが) Python の関数呼び出し時に、原則として再帰呼び出しが行われません。

こうした実装上の工夫や、 prickelpit.c などで追加されている pickle に関する処理により、 Stackless Python ではジェネレータを pickle することが可能となっているようです。

Stackless Python を選択する

主に、これらの技術的な理由により、個人的に好みの Ruby ではなく、 Stackless Python を選ぶことにしました。
また、 PyPy でも、同様にジェネレータを保存できるので、 Python を選んでおけば、仮に将来的に Stackless Python の開発が止まっても、ある程度の代替案が確保できるだろうという目論見がありました。

おそらく、 Scheme や Lua などでも同様のことができるのかもしれませんが、こういった理由により、 Pausable Unittest を実装する言語として、 Python を選択しました。

2017年1月21日土曜日

Ruby で net/http を使ってファイルなどを multipart で POST する

Ruby で net/http を使ってファイルなどを multipart で POST する

今回は、自分用の覚え書きです。

net/http を用いた multipart の POST

Ruby で HTTP クライアントを書いていると、しばしばファイルなどを POST したいことがあります。
この場合、 multipart なデータを POST することになります。

この機能は、標準ライブラリである net/http のみを用いて、割と簡単に実現できます。

よくネットで見つけられる set_form_data ではなく、set_form を使うのがポイントです。

set_form(params, enctype="application/x-www-form-urlencoded", formopt={}) の引数

params

set_form の一つ目の引数である params には、配列の配列を渡します。
それぞれの配列は、以下の形式になります。

[ (パラメータ名), (値 or ファイルオブジェクト), (オプションのハッシュ) ]

上の使用例を見れば簡単に理解できると思いますが、オプションのハッシュを指定することで、ファイル名や種類を指定することができます。

これらの値の組を配列として指定することで、送信するデータを決められます。

enctype

二つ目の引数である enctype には、 "multipart/form-data" を指定します。

デフォルトは "application/x-www-form-urlencoded" なので、ファイル送信するときは、必ず指定する必要があります。

formopt

三つ目の引数である formopt には、 :charset:boundary を指定することができます。

:boundary は、指定しなければランダムで URL-safe な文字列が生成されて使われます。
そのため、通常は特に指定する必要はないでしょう。

以上のような引数を指定し、 set_form を使うことで、簡単にファイルなどを multipart で送信することができます。

なぜか日本語のリファレンスから漏れており、(そのせいか) 日本語の記事があまりないようなので、メモとして書いておきます。
暇があったらリファレンスに載せてもらうようにお願いする予定です。