SyntaxHighlighter

2018年6月13日水曜日

Single Binary 版の Stackless Python 3.6.4

Single Binary 版の Stackless Python 3.6.4

Stackless Python が 3.6.4 対応されていたので、 1 バイナリ化した Stackless Python も対応してみました。

GitHub のページから、ダウンロードしてご利用ください。

1 バイナリ化した Stackless Python

以前に Stackless Python 2.7 を 1 バイナリ化しましたがその当時の変更を Python 3.6 に対応させることになります。
今回は、その際の対応と、そもそも 1 バイナリ化した Stackless Python はどういうものかについて、まとめようと思います。

1 バイナリ化した Stackless Python 作成の目的

業務の都合上、「種々の PC 実機上で簡単に Python を実行したい」という動機がありました。
それに加えて、 pausable unittest を実行したかったので、通常の Python ではなく Stackless Python を使うことにしました。

また、極力、通常の Python と比べて、実行速度が遅くならないようにしたいということも考えていました。
前に Ruby を OCRA で固めたときは、実行前の展開にやや時間がかかるのがストレスだったので、今回はそういったことがないようにしたいと思っていました。

1 バイナリ化した Stackless Python の変更点

デフォルトの Stackless Python から以下の変更を加えました。

  • プログラム引数の変更
  • embeddedimport の実装
  • 3rd party ライブラリの同梱
  • その他の小変更

プログラム引数の変更

1 バイナリ化した Stackless Python は、引数の意味を二つだけ変更してあります。

  • -B: この引数は意味が逆になっており、-B が存在しないときに、 *.pyc ファイルを作成しないようになっています。
  • -E: この引数も意味が逆になっており、-E が存在しないときに、環境変数を参照しないようになっています。

これらの変更は、いずれも未知の環境で動作しやすいようにしておくためです。
時刻が正しくない複数の実機で実行した場合や、他に Python の処理系がインストールされていた場合にも、正しく動作させるためです。

embeddedimport の実装

通常、 Python の import 文は、 sys.path に登録されたパスを順に探し、その中から外部モジュールをロードします。
また、 sys.path には python.zipも 登録されており、 ZIP ファイルの中もサーチの対象になっています。
この ZIP ファイルからのロードは、 zipimport というモジュールが担っています。

今回は、「一つのバイナリにしたい」という目標があったので、 zipimport を参考にして、 embeddedimport を作り、実装しています。

挙動の概要としては、以下のようになります。

  1. ビルド時に、 Lib 以下の *.py ファイルをすべて C 言語の文字列に変換して、 python のバイナリに組み込んでおきます。
  2. 実行時には、 sys.pathC:/path_to/python.exe のように python.exe のフルパスを登録しておきます。また、 sys.path_hooksembeddedimporter モジュールを登録しておきます。
  3. これにより、 import pickle などが処理される際に embeddedimporter によってモジュールのインポートが試みられます。
  4. embeddedimporter では、自身が保持している *.py ファイルの中から指定されたファイルを探し、それを実行します。

3rd party ライブラリの同梱

快適に使えるように、いくつかのライブラリを同梱してあります。

  • comtypes
  • pycodestyle
  • pyflakes
  • pyreadline
  • PyYAML with libyaml

PyYAML に関しては、 libyaml をビルドしておき、 C 言語のサポートがあった方がずっと速いので、そのようにしてみました。

一点、注意としては、 libyaml とのブリッジライブラリである _yaml は、 Cython 形式になっており、 C 言語としてビルドするには前処理が必要でした。
Stackless Python の場合、 Fast Calll 時の関数呼び出しの処理が変更されているので、 CYTHON_FAST_PYCALL を 0 に設定し、 _yaml から Fast Call させないようにすることが必要でした。

その他の小変更

プログラムの引数を変更した関係で、いくつかのテストを変更しました。 -E を使っているテストは非常に多かったので、その辺りを中心に変更しました。

また、 C 言語で書かれた拡張ライブラリ *.pyd を、静的に Python にリンクするために、ビルド方法や config.c でのビルトインモジュールの定義部分を変更しました。

私の業務の都合上、 XML の属性をソートしたくなかったので、 xml.etree.ElementTree の出力部分に少し手を加えました。
具体的には ElementTree.writesort_attrib=True というキーワード引数を追加し、ソートを制御できるようにしておきました。

これらの対応を行い、テストがほぼ通ったので、ついにリリースになりました。
Windpws PE 上での作業や、多数のテスト実機上で Python を走らせる必要がある場合などに、ご利用いただければと思います。

2018年3月8日木曜日

Windows 上で一時的に Python を使って作業する際に使っているバッチファイル

Windows 上で一時的に Python を使って作業する際に使っているバッチファイル

私は、仕事でクリーンな (OS 展開直後の) Windows 実機や Windows PE 上で、Python を用いて作業をすることがしばしばあります。
今回は、その際によく使っているスクリプトを備忘録としてまとめておきます。

いずれも、 Python の -x オプションを使って、バッチファイル先頭行の goto 文をスキップしています。

1バイナリの Python

前提として、1バイナリ化した Python を使います。

以前に書いたように、ほぼすべての標準ライブラリを含んだ StacklessPython を、単一の EXE ファイルをコピーするだけで動作するようにしてあります。
こういった用途のために作ったので、インストールが不要かつコピーするだけで使えるようにしました。

管理者権限で実行する Python スクリプト

下記のようなバッチファイルを用意し、print("hoge") 部分を好きな Python スクリプトに変更すれば、ダブルクリックするだけで、管理者権限の Python スクリプトとして実行することができます。
なお、同じディレクトリに python.exe が必要です。

readline を有効にした状態で Python インタプリタを立ち上げるスクリプト

こちらも、下記のようなバッチファイルを用意し、ダブルクリックすれば、 Python のインタプリタが立ち上がります。
pyreadline を使っているので、 Ctrl-f などのキーバインドや、補完、ヒストリ保存もできます。

作業の内容によっては使えると思いますので、置いておきます。

2017年12月16日土曜日

Outlook のキーワード強調表示用のフォントを作成する

Outlook のキーワード強調表示用のフォントを作成する

LigatureYourName

前回の Outlook でキーワードハイライトを実現する方法の続きです。

LigatureYourName というサイトでフォントを作成することができます。
せっかくなので、 Heroku で WEB サービスとして作成してみました。

フォントの情報

ベースフォント
ベースとなるフォントは、源真ゴシックです。
ライセンス
源真ゴシックに準じ、 SIL Open Font License 1.1 のもとに使用することができます。

その他

  • アルファベットなど、日本語以外の文字ではうまく強調表示されないことがあるようです。
    アプリケーションの描画方法に依存するようですが、理由をご存知でしたら教えてください。
  • 種類は、デフォルトで OpenType (PostScript) です。
    これは、 Windows10 の Creators Update より前では、TrueType 形式のフォントは汚く表示されることがあるためです。
    TrueType 形式が必要な場合、「詳細」欄で選択することができます。
  • このフォントの利用により生じたいかなる損害についても、一切責任を負いません。
    ご自身の責任の範囲でお使いください。

次回は、 LigatureYourName のサイトを作った際の技術的な内容について、まとめる予定です。

Outlook で本文中の固定キーワードを強調表示する方法

Outlook で本文中の固定キーワードを強調表示する方法

はじめに

Outlook は広く使われているメーラーの一つではないかと思いますが、なぜかキーワードハイライトができないようです。
例えば、自分の名前を本文中で常にハイライトしたいのですが、私が探した限り、そういった方法は見つかりませんでした。

以前に使ったことがある Becky!Thunderbird では標準機能やプラグインでサポートされていますので、不便に感じていました。

今回は Outlook で固定キーワードを強調表示する方法についてまとめます。

キーワードハイライトの仕組み

専用フォントによるハイライト

Outlook 用に、専用のフォントを入れ、そのフォントで表示することで実現します。
そのフォントでは、あらかじめ強調したいキーワードの文字だけを太字にしておくなどすることで、疑似的にキーワードハイライトすることができます。
例えば、「ハイライト」というキーワードを白黒反転で強調するような場合、以下のようになります。

このように、あらかじめ指定した「ハイライト」が指定した白黒反転で強調表示されています。
サンプルページで実際の挙動を確認できます。

リガチャ (合字) によるキーワード指定

前掲した例を見ると分かりますが、「ライト」は強調表示されていません。
これは、フォントの「リガチャ (合字)」機能を使って実現しているためです。

Wikipedia の合字ページを見ると分かりますが、リガチャとは欧文などで特定の並びの文字に専用のフォントを指定できるようにする機能です。
よく目にする例では、ff の並びに対し、二つの f がいい具合に合わさったものがあります。

このフォントの機能を活用し、日本語でも特定の並びの文字に対してリガチャを作成することで、疑似的なキーワードハイライトを実現することができます。
今回の例では、「ハイライト」を白黒反転したものを一つのグリフ (字形) として作成し、それを「ハイライト」の文字の並びに対して使うように、リガチャ指定しています。
こういった仕組みのため、部分文字列の「ライト」が強調表示されることはありません。

制限事項

この仕組みでキーワードハイライトを実現する場合、以下のような制限事項があります。

強調するキーワードを動的に変更できない
強調するキーワードを変更する場合、フォントを生成しなおす必要があるので、動的にキーワードを変更することはできません。
リガチャをサポートしている Outlook でのみ使える
具体的には Outlook 2010 以降が必要です。
(Outlook で使う場合) HTML メールの強調には使えない
テキストメールの読み書きに対してしか使えません。
改行がはさまるとリガチャにならない
改行が挟まった場合は、通常の文字として表示されます。

次回は、実際にフォントを作成する方法についてまとめます。

2017年10月27日金曜日

Nyagos の拡張 (主に Subversion 用 Lua スクリプト)

Nyagos の拡張 (主に Subversion 用 Lua スクリプト)

前回は Nyagos を tcsh 風に使うようにするための変更についてまとめました。
今回は、それに加えてもう少し Subversion まわりについて拡張してみました。

できること

作成した vcs_info.lua で、以下のことができます。

  • svn ls https://github.com/masamitsu-murase/nyagos.git/trunk/[Tab] のように、 svn で始まる場合に URL のパスに対しても補完することができます。
  • VcsInfo.box_svn_revisions(path) のように呼ぶことで、 C-x C-g の git のリビジョン選択のように、 Subversion のリビジョンを選択することができます。
  • VcsInfo.check_vcs_branch() で、現在のパスが svn, git, hg のいずれかを判別し、返してくれます。
    また、 git, hg の場合はブランチ名を、 svn の場合は親ディレクトリの名前を、二つ目の戻り値として返してくれます。

必要なファイルは nyagos.d/catalog/vcs_info.lua として GitHub に置いてあります。
このファイルを nyagos.d/catalog 以下に置き、 .nyagos でいくつか設定をします。

Subversion の URL の補完

この機能を使うには、 vcs_info.lua.nyagos で use するだけです。
.nyagos の例を見てください。

このスクリプトを使うと、svn で始まるときに限り、後続の URL を補完してくれます。

svn ls コマンドで、リポジトリのディレクトリを見ながら補完するので、環境によってはやや遅いですが、私としてはそれなりに快適に使えています。

また、デフォルトでは直前の補完結果を 10秒間はキャッシュしていますので、連続で Tab を押したときは軽快に動くようになっています。

Subversion のリビジョンの選択

コマンドラインでマージをする際に、マージ対象のリビジョンを手で入力するのは面倒です。
git の場合は、 C-x C-g でログを表示し、ハッシュ値を入力することができますが、これと同じようなことができることを目指しました。

この機能を使う場合、 box.lua のコードを参考に、以下のようなコードを .nyagos に追加する必要があります。

上記のように、 VcsInfo.find_svn_path で現在のコマンドライン入力の中から URL らしきものを探し、 VcsInfo.box_svn_revisions でリビジョンを選択することができます。

git, svn, hg のブランチ名の取得

VcsInfo.check_vcs_branch を呼ぶことで、現在のディレクトリのブランチ名を取得することができます。

以下のようなルールでブランチ名を取得しています。

git, hg
git rev-parse --abbrev-ref HEADhg branch でブランチ名を取得しています。
svn
現在のディレクトリを含むチェックアウトツリーのルートディレクトリを探し、その一つ上のディレクトリ名を取得します。
例えば、http://example.com/trunk をチェックアウトしたディレクトリ以下なら、trunk を返します。
同様に、http://example.com/branches/feature_a をチェックアウトしたディレクトリ以下なら、 feature_a を返します。

私は、この機能を用いて以下のようにプロンプトを表示しています。

このスクリプトだと、以下のようにプロンプトが表示されます。

もちろん、 Subversion のディレクトリ名が日本語を含む場合でも、適切に動作します。

Nyagos は Lua で拡張できるおかげで、割と楽に拡張することができました。

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 における注意点