SyntaxHighlighter

2016年11月4日金曜日

Open TortoiseSVN の Native Messaging 対応 (Firefox)

Open TortoiseSVN の Native Messaging 対応 (Firefox)

Firefox 向けの Open TortoiseSVN を WebExtensions 化したので、そのまとめと思うことをダラダラと書きます。

Firefox の WebExtensions

Firefox では、従来の形式のアドオンを将来的に廃止し、 WebExtensions という新しい形式のアドオンへ移行しようとしています。

WebExtensions は、内容は Google Chrome の拡張機能とほぼ同じものです。
従来のアドオンとの大きな違いは、アドオンでできることの自由度です。
従来のアドオンでは、 Firefox を構成する XUL, JavaScript の非常に幅広い部分を自由にカスタマイズできたのに対し、WebExtensions では、あらかじめ Firefox 側から提供された部分しか拡張できません。

ブラウザを作る側からすれば、あまりに自由にアドオン側にカスタマイズを許すと、互換性を保ちながら内部構造を大きく変更することが困難になり、マルチプロセス化など大規模なコード変更ができなくなるなどの弊害があったようです。

Firefox ユーザーの中には、「自由度が減る」ことを嫌って WebExtensions への流れに反発している人も多いようです。
個人的にはこの心情はよく理解できます。
私もそうなのですが、今でも Firefox を使っている人には、 Firefox の「自由なウェブブラウザ」という考え方を好む人がいるように思います。
そういった場合には、アドオン作成者の自由度が減る方向になるのは、受け入れづらいのだろうと思います。
ブラウザをメンテナンスする側の気持ちとしてはよく分かるのですが。

WebExtensions の Native Messaging での注意点

Open TortoiseSVN では、外部ツールである TortoiseSVN を開く必要があります。
Google Chrome 版と同様に、 Firefox の WebExtensions では、 Firefox 50 以降でサポートされた Native Messaging を使う必要があります。

Google Chrome 版とほぼ同様なのですが、一点だけ注意する必要がありました。
それは CreateProcess 関数へのフラグ指定です。

Firefox の Native Messaging で起動されるプログラムから、さらに外部プログラムを実行する場合、Windows では CreateProcess で起動することが多いと思います。
このとき、 CreateProcessdwCreationFlags として CREATE_BREAKAWAY_FROM_JOB を指定する必要があります。
より正確には、 Firefox 50 では不要なのですが、 Firefox 51 では必要となるので、注意が必要です。

Firefox 51 で Bug 1290598 - Migrate native messaging tests to xpcshell への対応の一つとして Manage and terminate Windows subprocess trees as a single job object. の変更が加えられました。
これにより、 Native Messaging で起動された外部プログラムのプロセスは、ある一つの Job Object に結びつけられることになります。
よって、その Job Object が Terminate されるタイミングで、 CreateProcess で起動された子や孫のプロセスも、同時に終了されることになります。

CREATE_BREAKAWAY_FROM_JOB を指定しない場合、起動した TortoiseSVN は、 Job Object の終了とともにプロセスが終了されることになり、うまく動作しませんでした。
CREATE_BREAKAWAY_FROM_JOB を指定することで、 TortoiseProc.exe を Job Object に関連付けず、プロセスの終了を防ぐことができます。

これは MDN Native Messaging の Closing the native app に書かれている内容ですが、知らずに Google Chrome から移植すると、何が起こっているのか分からずかなり悩むことになりました。

なお、同様に悩まれた方が Bug 1301246 - extension will close native client and sub processes those native client created で議論されており、結果として上記の MDN Native Messaging に注釈が加えられたようです。

こういった Google Chrome 拡張機能と Firefox のアドオンの微妙な違いは、今後も残ってしまうのでしょうか。
ちょっと面倒です。

2016年9月22日木曜日

Pausable Unittest on EFI Stackless Python - PyCon JP 2016 での発表を終えて

Pausable Unittest on EFI Stackless Python - PyCon JP 2016 での発表を終えて

PyCon JP 2016 で発表する機会をもらいました。
Pausable Unittest on EFI Stackless Python というタイトルで発表してきました。

発表資料

発表資料を公開します。
ちょっとだけアニメーションが崩れていますが、さすがに Microsoft 純正だけあって docs.com の再現はすばらしいです。

発表動画

発表の様子はこちらの動画から見られます。

発表に至る経緯と、簡単な感想

経緯

一度くらい社外のカンファレンスに出たいという気持ちがあったので、ちょうどいいネタがあったこともあり、 Python 歴は半年ぐらいですが、 talk に申し込んでみました。
また、開発職についたこともあって、大学時代と異なり外で発表する機会がなかったので、社外の雰囲気やトレンドを知っておきたい、ということも動機の一つでした。

talk への申し込みは、学会の発表を簡単にしたようなもので、簡単なプロポーザルと自分の紹介などを書き、採択会議の方で採択/非採択 が決まる、といった形でした。
採択率は 48/108 とのことで、「適当に出すと通らないが、そこそこきちんと書けば通る可能性も高い」といったところだと思いました。

感想

初めての、学会以外のカンファレンスでの発表でした。
残念ながら、聴衆の方の人数はそれほど多くはなかったのですが、その分、特に緊張することもなく発表を進めることができました。

発表では、一点だけ想定外のことが起こりました。
VirtualBox 上で、その場でデモをしようとしていたのですが、 VirtualBox 上の OS を再起動すると、なぜか VirtualBox のウィンドウがプロジェクター側から消えて内蔵液晶側に移動してしまう、ということが起こりました。
私のデモでは再起動を繰り返すので、致命的だったのですが、用意していたムービーに切り替えることで、そのまま進めることができました。
念の為と思って作っておいたムービーでしたが、あって助かりました。

また、聴衆の方にはきちんと発表の内容は伝わったようで、いただいた質問も非常に鋭いものでした。大変ありがとうございました。
こういった直接のやり取りができるのは、カンファレンスで発表することの、大きなメリットなのだろうと思います。

発表冒頭でアイスブレイク的に触れたのですが、私は基本的には Ruby が好きで、 Python を触り始めたのは半年ほど前のことでした。
そのため、 Python の文法は分かりますが、コミュニティの文化はよく分かっておらず、カンファレンスへの参加はやや緊張するものがありました。
いくつかの発表は、毎年の恒例になっているものもあるそうですが、そういったノリにはついていけず、とまどうこともありました。
ただ、私のような新規参加者が壁を感じるかといわれると、そうではなく、非常によい雰囲気だったと思います。

今回、私が発表したライブラリは、「unittest を一時的に中断し、後で続きを継続する」というコンセプトなのですが、似たようなことをしている David Weil さんに話を聞けたのはとても有意義でした。
彼の発表は私の発表と時間が重なっていたので、彼にお願いして昼休みに個人的に発表を聞かせてもらいました。
知りたかったことも直接の質問で確認することができたり、お互いに意見交換して発表スライドをその場で充実させたりと、よい経験ができました。

そういうわけで、初めての PyCon JP への参加は、とても楽しいものとなりました。
スタッフの皆様を始め、 PyCon JP に関わられた方々、大変ありがとうございました。

2016年5月7日土曜日

Pausable Unittest その1 (continulet と tasklet)

Pausable Unittest その1 (continulet と tasklet)

概要

初めて Python 向けのライブラリを書きました。
Pausable Unittest という名前で、名前の通り unittest の亜種です。

このライブラリは、通常の unittest と異なり、途中で中断することができます。

今回は、このライブラリの目的と、利用している PyPy, Stackless Python の技術についてまとめます。

目的

PC に限らず、家電や組み込みデバイスなどの開発をする場合、再起動をはさんだ処理の挙動を確認したいことが、しばしばあります。
例えば、特定の条件下で起動時にハングアップしないかや、再起動後に種々のレジスタが正しく設定されているかをチェックする場合などです。

こういった場合、既存のテストフレームワークでは、テストを書くのが難しいことが多いです。
例えば、実際には動きませんが、 Python の unittest なら、下記のように書けると嬉しいです。

意図としては、(*1) でレジスタ値を読み、(*2) で再起動し、 (*3) で再度レジスタ値を読む、ということを実現しようとしています。
(*1)reg1 変数に格納した結果を、 (*2) のシステム再起動後の (*4) でも参照できるようにしたいところがポイントです。

pausable_unittest

上記のようにテストスクリプトを書けるようにしたのが、 pausable_unittest です。
下記のように、 pausable_unittest.TestCase を継承することで、再起動をはさんでもテストを続けることができます。

ただし現在のところ、 Stackless PythonPyPy でしか動作しません。
また、 (*1) でファイルなどを開いた状態で (*2) の reboot を呼ぶと、エラーで停止します。
これらは実装上の都合によるものです。後述するように pausable_unittest では、現在の状態を pickle を用いてシリアライズするので、シリアライズできないオブジェクト (ファイルなど) が変数に保持されていると、状態の保存に失敗します。

pausable_unittest の仕組み

Stackless Python と PyPy でやや異なりますが、 PyPy の continulet が基本になっていますので、そちらから説明します。

continulet

PyPy には、 continulet というオブジェクトがあります。

continulet は軽量スレッドの一種ですが、ユーザーが明示的に continulet の中断/切り替え操作をしないと、continulet (軽量スレッド) が切り替わることはありません。
この点で Ruby の Fiber とほぼ同じものです。

また、 continulet はシリアライズすることができます。
つまり、中断状態の continulet を、実行中の関数の変数の値などを含めて、すべてファイルに書き出すことができます。当然、ファイルに書き出した状態を、後で復旧させることもできます。

この機能を用いて、下記のようなことが実現できます。

このスクリプトは以下のように実行されます。

  1. func 関数を実行する continulet を作成します。
    この時点では、まだ func は実行されません。
  2. c1.switch() を呼び、 func に制御を移します。
    その結果、 func が実行されます。
    func の中で con.switch() が呼ばれると func の制御を中断し、制御を main に戻します。
  3. 続いて、 pickle で中断状態を含めて continulet をシリアライズします。
  4. シリアライズされた文字列から、 continulet を復旧させています。
  5. 復旧させた continulet に対して switch を呼び、 func に制御を移します。
    このとき、前回に中断した状態から実行が再開されます。今回の例では、 for 文の中から再開されます。
    ローカル変数 i の値も含めて、前回に中断した状態から再開されます。

tasklet

一方で、 Stackless Python には似たような仕組みとして tasklet があります。
PyPy の continulet とややインターフェースは異なりますが、中断状態の tasklet を保存できるなど、ほぼ同じような特徴を持っています。

pausable_unittest では、 tasklet を continulet 風に使うためのラッパーオブジェクト pausable_unittest.continulet を用意して、 PyPy でも Stackless Python でも同じように動作させられるようにしています。
ただし、あくまで pausable_unittest で使うためだけのものなので、複数の continulet がある場合などをエミュレートするようにはできていません。

この、「中断状態をシリアライズし、後で復旧させられる」という特徴を活用することで、 pausable_unittest では楽に再起動後をまたいだ処理を書くことができます。

余談ですが、私は Ruby が好きなので、 Ruby の Fiber に同様の機能があれば、おそらく Ruby で実装していただろうと思います。

次回は、これらを使ってどのように pausable_unittest を実装したかについて書く予定です。

という予定だったのですが、 PyCon JP 2016 で発表させていただくことになったので、それが終わったらまとめようと思います。

2016年4月3日日曜日

Stackless Python と 1 バイナリの python.exe

Stackless Python と 1 バイナリの python.exe

仕事で Stackless Python を 1 バイナリの exe にして使ってみようと思ったので、少し整理してみました。
Python 2.7.9 に基づいたものですが、1 バイナリにしたものを python.exe からダウンロードできるようにしてあります。

また、これまで割と Ruby を使うことが多かったのですが、 Stackless Python のとある特長を活用したかったので、勉強してみました。

Stackless Python

Python には様々な亜種がありますが、その中の一つ Stackless PythonPyPy は、「tasklet や continulet を pickle できる」という大きな特徴があります。
他のプログラミング言語で言うと、 JavaScript のジェネレータや Ruby の Fiber を、保存可能な形式にシリアライズできる、ということです。

これができるということは、スクリプトを途中まで実行して中断し、後からその続きを実行する、ということが実現できます。
これが実現したかったので、今回は Stackless Python を使ってみました。

この処理は、オリジナルの Python では実現できませんが、 Stackless Python では少し Python インタプリタを変更することで、これを実現しています。

また、オリジナルの Ruby でも、実現ができません。
実装上の種々の理由がありますが、 Ruby では例外の捕捉と生成に setjmplongjmp を使っているので、なかなか難しいだろうと思います。

1 バイナリの python.exe と embeddedimport

利便性から、この Stackless Python を、できれば 1 バイナリにしておこうと思いました。
これは、python.exe hoge.py のように python.exe のみがあればスクリプトを実行できた方が、私の環境では都合がよかったからです。

そのため、 Stackless Python をスタティックビルドするとともに、既存の標準添付ライブラリの .py ファイルを、すべて python.exe の中に組み込むことにしました。

embeddedimport という名前のライブラリが、これを実現しています。
原理的には、標準ライブラリの zipimport と似たような仕組みで、 python.exe の中に静的に持っている .py ファイルの内容をロードしています。

このようにして得られた python.exe を BitBucket の python.exe に置いておきました。
また、 embeddedimport については、別途、時間があればまとめようと思います。

近頃、忙しいのでなかなかプログラミングができません。

2016年2月13日土曜日

Outlook 2010 で外部エディタでメールを編集するマクロ

Outlook 2010 で外部エディタでメールを編集するマクロ

メーラーとして Outlook 2010 を使う場合に、メールを外部のテキストエディタで編集するマクロを作りました。
GitHub で External Editor for Outlook 2010 として公開していますので、よければお使いください。

概要

Outlook 2010 では、メールの編集画面として、 Word のような独自の編集画面が用意されています。
他のメーラーと比べて特に劣っているわけではないと思いますが、私はテキストエディタの方が使いなれているので、できれば編集は好きなエディタでやりたいです。

そこで、外部テキストエディタでメールを編集できる VBA マクロを作ってみました。

マクロの仕組み

このマクロは、 Using Vim as an External Editor with Outlook のマクロを元に、種々の変更を加えて作りました。

下記のような動作になっています。

  1. 現在アクティブなメールエディタの本文を、指定されたディレクトリの下に一時ファイルとして保存する。
  2. そのファイルを引数として、外部のエディタを CreateProcess API で実行する。
  3. SetTimer API で、定期的にエディタのプロセスが終了したかどうかをチェックする。
  4. プロセスが終了したら、一時ファイルの内容をメールエディタの本文に反映させる。

元のマクロと大きく異なる部分は、上記の 3 の SetTimer で待つ部分です。

元のマクロでは、 DoEvents を呼びながらプロセスの終了を待ち続けていたのですが、この実装では、なぜか Outlook 側で他のフォルダを開くことができません。
理由はよく分かりませんが、 Outlook 2010 では、 VBA のプロシージャを抜けるまではフォルダの切り替えができないようです。
そのため、 SetTimer API で待ってみることにしました。

素朴な疑問

いつも疑問に思うのですが、 Microsoft の技術者は Outlook を使っているのでしょうか。

Outlook は一般ユーザー向けには悪くないと思いますが、あまりプログラマーに好まれるものではないように思います。
マクロ言語の VBA は、プログラミング言語として洗練されているとは言い難く、 Outlook 自体のメール仕分けも正規表現が使えないなど、プログラマが不満を持ちそうなポイントが多くあるように感じます。

誰か Microsoft の内情を知っていたら教えてください。

2015年2月21日土曜日

CF-RZ4 タッチパッドでチャームを出さないようにする方法

CF-RZ4 タッチパッドでチャームを出さないようにする方法

先日、 Panasonic のノートパソコン CF-RZ4 を買いました。

ノートパソコンで初めて Windows 8.1 を買ったのですが、タッチパッドの右端を中央方向にスワイプすると、 Windows 8.1 のチャームが出てきます。
便利な人にはよいのでしょうが、私は不便に感じるので、止めました。

せっかくなので、止め方を書いておきます。
なお、レジストリを触るので、あまり知識が無い人は試さないでください。当たり前ですが、失敗して起動しなくなったりしても、自己責任でお願いします。

  1. regedit.exe でレジストリエディタを立ち上げる。
  2. HKEY_CURRENT_USER\Software\Synaptics\SynTPEnh\ZoneConfig\TouchPadSMB2c の下にある GlobalEdgeSwipeEnable0 に変更する。
  3. ログアウトし、ログインしなおす。

これで、チャームが出なくなると思います。

2014年11月30日日曜日

Open TortoiseSVN for Google Chrome の Native Messaging Host 対応

Open TortoiseSVN for Google Chrome の Native Messaging Host 対応

前に書いたように、Google Chrome には、登録済みのローカルファイルを実行する仕組みがあり、Native Messaging と呼ばれています。
今回は、この具体的な作り方について書きます。

Native Messaging を使う Google Chrome 拡張機能

Chrome 拡張で Native Messaging を使うには、大きく分けて以下の対応が必要です。
なお、公式のページに、まとまった情報があります。

  • JSON 形式でやりとりする実行ファイルの準備
  • 実行ファイルのマニフェストファイルの準備
  • Chrome 拡張機能から、上記の実行ファイルの呼び出し

JSON 形式でやりとりする実行ファイル

やりとりするデータ形式

Native Messaging では、JavaScript 側と実行ファイル側が JSON 形式でデータをやりとりします。
JavaScript 側から渡すデータは JSON 形式にシリアライズされ、実行ファイルの標準入力から渡されます。
逆に、実行ファイル側からは、標準出力に JSON 形式でシリアライズしたデータを出力することで、JavaScript 側にデータを渡すことができます。

ただし、JSON データの長さ 4byte が、後にくる文字列の前に配置されます。

例えば、{"a":1234} というオブジェクトを、実行ファイル側から渡すことを考えます。
この JSON データの長さは 10 (0Ah) byte です。この場合、0A 00 00 00という 4byte が出力され、その後に JSON データが続きます。
サイズを示す先頭の 4byte は、Native Byte Order で渡すので、大抵の PC では Little Endian になります。

データ列の例
0A000000'{''"''a''"'':''1''2''3''4''}'

そのため、受け取る側はまず最初の 4byte を読み、そのサイズ分のデータを続けて読むことになります。

逆に実行ファイル側から出力する場合も同様です。

注意点

Windows 特有の注意点として、「標準入力と標準出力をバイナリモードで開く必要がある」ということがあります。

Windows では、ファイルを処理する際に「テキストモード」と「バイナリモード」があります。
説明は省きますが、標準入力や標準出力は「テキストモード」になっているので、その状態で \n を出力すると \r\n に自動で変換されることになります。
先の例では、0Ah\n なので、以下のようなバイト列になります。

データ列が変換される例
0D0A000000'{''"''a''"'':''1''2''3''4''}'

結果として、データを受け取る Chrome 側は 00000A0Dh つまり 2573byte の JSON データが送られてくると判断し、正しく動かなくなります。

そのため、C 言語であれば、以下のような Windows 依存のコードを実行しておくことで、標準入力や標準出力をバイナリモードにしておく必要があります。

#include <io.h>
#include <fcntl.h>

void set_binary_mode()
{
    _setmode(_fileno(stdin), _O_BINARY);
    _setmode(_fileno(stdout), _O_BINARY);
}

これらに注意して、好きな実行ファイルを作ります。

Open TortoiseSVN for Google Chrome の場合、 open_tortoise_svn_host.exe がその実行ファイルですが、この中では JSON を扱うために json11 を使わせてもらいました。
このライブラリは C++11 向けに書かれており、少し変更するだけで Visual Studio 2013 の C++ コンパイラでもコンパイルできます。

実行ファイルのマニフェストファイルの準備

作成した実行ファイルは、そのままでは Chrome から呼び出すことはできません。
呼び出せるようにするために、マニフェストファイルを作り、そのマニフェストファイルを Chrome に知らせる必要があります。

マニフェストファイル

公式のページにあるように、マニフェストファイルは以下のように定義します。

{
  "name": "com.my_company.my_application",
  "description": "My Application",
  "path": "chrome_native_messaging_host.exe",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"
  ]
}

上記のようなマニフェストファイルを任意のファイル名で作成します。
name は、この Native Messaging 機能の名前を任意で決めて指定します。
path には実行ファイルの名前を指定します。実行ファイルは絶対パスで指定しますが、Windows ではマニフェストファイルがあるディレクトリからの相対パスでも構いません。
type は、現状 stdio しか指定できません。これは上で説明したような JSON 形式のデータを標準入力と標準出力でやりとりすることを意味しています。
allowed_origins には、この Native Messaging Host にアクセスしてよい拡張機能の名前を指定します。この名称は、拡張機能一覧のページ (chrome://extensions/) で ID をチェックすることで分かります。

というか、公式のページの説明を見ましょう。

マニフェストファイルの登録

続いて、作成したマニフェストファイルを Chrome に知らせる必要があります。

これも公式のページに書いてあるように、Windows の場合はレジストリで指定します。
HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application にマニフェストファイルの絶対パスを書き込めばよいです。
HKEY_LOCAL_MACHINE 以下でもよいですが、HKEY_CURRENT_USER の下の方が管理者権限が不要なので楽でしょう。

Chrome 拡張機能から、上記の実行ファイルの呼び出し

Native Messaging には、実行ファイルが常駐するタイプと、毎回起動するタイプがあります。
Open TortoiseSVN for Google Chrome は後者のタイプです。

この場合、下記の公式ページの例のように sendNativeMessage を用いて、送信した JSON データと、受け取った JSON データを処理するためのコールバック関数を引数として渡すことで、好きな処理をすることができます。

chrome.runtime.sendNativeMessage('com.my_company.my_application',
  { text: "Hello" },
  function(response) {
    console.log("Received " + response);
  });

面倒になってきたので後半は省略しましたが、作成するのは簡単なのでローカルの実行ファイルと Chrome を連携させたい場合は利用するとよい機能だと思います。