SyntaxHighlighter

2020年8月3日月曜日

nginx と Flask を使った Git LFS サーバー

nginx と Flask を使った Git LFS サーバー

Git LFS サーバーを自前で用意する必要が生じたのですが、手頃なものが見つからなかったので、手作りで作ってみることにしました。
Git LFS サーバーの仕様自体はシンプルで、とりあえず動作させるぐらいであればすぐに作れたので、今後のために記録を残しておこうと思います。

また、今さらですが Docker にも慣れておきたかったので、 Docker のイメージにしてみました。本来は docker-compose を使うようなケースのような気もしますが、とりあえず単一の Docker コンテナにしてあります。

なお、作成したサーバーのソースコードなどは、 gitlfs_with_nginx として GitHub 上に置いてあります。

Git LFS サーバー

Git LFS は、 Git で大きめのバイナリファイルを管理するために GitHub が中心となって策定した Git の拡張機能です。

概要としては、以下のようになるかと思います。

  • あらかじめ指定したファイルを、 Git リポジトリではなく、別に指定した Git LFS サーバーに保存する。
    ファイルは、ファイル固有の oid (現状ではファイルの SHA256 値) と紐付けて、 Git LFS サーバー内で Key-Value ストアのように保存される。
  • 元の Git リポジトリには、実体となるファイルの oid などが書かれたポインターファイルのみを保存しておき、 checkout などの際に、適宜 Git LFS サーバーからファイルの実体をダウンロードしてくる。
  • これらのファイルの保存やダウンロードの動作は、 Git のフックなどで実現されており、ユーザーはほとんど意識することなく、通常の Git リポジトリで作業する感覚で作業することができる。

ちなみに、私は Git LFS を最初に使ったとき、「Git LFS サーバーで管理されているファイルだけ、 Subversion のようなタイミングでダウンロードされるんだな」と感じました。

それはともかくとして、 Git LFS サーバーは比較的シンプルな仕様の HTTP サーバーです。
Batch APIBasic transfer をサポートするだけであれば、(どこまで異常系を考えるかによりますが) それほど悩まずに実装することができると思います。

Flask と nginx による Git LFS サーバー

そこで、 Git LFS サーバーの理解を深めるのを兼ねて、 Python の Flask フレームワークを使って実装してみました。
また、 nginx をリバースプロキシとして使うことで、パフォーマンスを向上させています。
以下に概要のみ、記します。 これらの処理は lfs_server.py に書かれています。

batch エンドポイント (batch メソッド)

Batch API の仕様に従って、 download, uploadoperation 要求に対し、指定された oid のファイルの実体をダウンロードもしくはアップロードするための URL を返します。

このとき、ファイルの実体をダウンロード/アップロードする際の GET/PUT リクエスト時に送信してもらうヘッダを同時に返します。
このヘッダには認証用のデータなどを含めることができます。
今回の実装でも、リポジトリ名や認証期限などを blake2b でハッシュをとったものをヘッダに追加しています。

ダウンロード用エンドポイント (download_file メソッド)

ダウンロードは、基本的に nginx に処理を任せます。
Flask 側では認証処理をしたあと、 X-Accel-Redirect ヘッダを指定し、 nginx 側で静的なファイルを配信するように構成することで、 Flask 側でファイル配信しなくてすむようにしています。

具体的には、 X-Accel-Redirect ヘッダに /repos/repository_name/oid のようなパスを指定することで、 nginx から静的なファイルを配信できるようにしています。

アップロード用エンドポイント (upload_file メソッド)

アップロードも、基本的に nginx に処理を任せます。
nginx 側で client_body_temp_path, client_body_in_file_only などを使うことで、クライアント側から PUT されてきたファイルを、 nginx がファイルに保存するようにします。
そして、そのファイル名を Flask 側に渡すことで、 Flask 側では渡されたファイルを oid で一意に決まる所定の場所に移動させます。
Flask 側はファイルの移動を行うだけなので、処理の負荷が少なくてすみます。

Docker での実装

上記のような仕組みの Flask サーバーの実装や nginx の設定を行い、 Docker イメージとして構築してみました。

env.list としていくつかのパラメータは変更できるようになっており、社内利用ぐらいであれば、 Basic 認証を追加するぐらいで使えなくはないのではないかと思います。

2019年12月31日火曜日

Windows 上の Python で OpenVINO を動かす

Windows 上の Python で OpenVINO を動かす

今回は、 Windows 上の Python で、 Intel からリリースされている AI 推論エンジン OpenVINO を動作させます。
といっても、OpenVINO ツールキットそのもののインストールは不要です。
Python の pip ライブラリをインストールするだけで試すことができます。

Intel OpenVINO

OpenVINO は Intel によって開発されている、エッジ端末上で AI 推論を行うためのライブラリです。
あくまで推論に特化しており、学習は別のツールで行うことが前提です。
また、推論エンジンは Intel CPU, GPU, GNA, VPU, FPGA などに対して高度に最適化されています。

事前準備

Python のインストール

Windows 上で、 Python 3.5, 3.6, 3.7 のいずれかをインストールしておきます。
64bit 版をインストールしておく必要があります。
現時点では、 Python 3.8 には対応していません。

OpenVINO の pip ライブラリのインストール

元々の OpenVINO は、現時点では pip ライブラリになっていないようです
そのため、私が Windows 向けに非公式な pip ライブラリ (wheel パッケージ) を作っておきました。
今回はこのライブラリをインストールします。
なお、後に公式ライブラリが公開される場合にややこしくなるので、公式の PyPI には登録していません。

PyPI に登録されていないので、下記のようにインストールします。

なお、 openvino-python をインストールすると、 cv2 モジュールも使えるようになります。そのため、別途 opencv-python などをインストールする必要はありません。

また、 numpy も必要なのでインストールしておきます。

パスの設定

openvino-python を動作させる際に必要な DLL は、 pip によるインストールの際に所定のディレクトリにダウンロードされています。
venv 環境の場合、 venv/Library/bin の下にダウンロードされます。
そのため、このディレクトリを PATH の先頭に追加しておきます。

venv の場合、 venv/Scripts/activate.bat の末尾を以下のように変更しておくとよいと思います。

動作確認

上記のように事前準備ができ、パスが通った状態であれば、下記のスクリプトがエラーなく実行できるはずです。

これで、 IENetwork など、種々の OpenVINO のクラスを使うことができます。

次回は、実際に Pre-trained なモデルを使い、簡単な動作サンプルを作ってみようと思います。

2018年11月24日土曜日

1バイナリの Python と exepy

1バイナリの Python と exepy

1バイナリにした Stackless Python の更新もそろそろ終わりなので、その中に含まれている exepy ライブラリについてまとめておこうと思います。

1バイナリの Python の仕組み

この Python は、 外部 DLL や *.py ファイルが不要になっています。
DLL については、ビルド時に tcl/tk, OpenSSL などを含め、静的リンクすることで解決しています。
*.py ファイルについては、 embeddedimport というモジュールを新規に開発することで対応しています。

これらについては Single Binary 版の Stackless Python 3.6.4Single Binary StacklessPython 3.6.4 L8 リリースに簡単にまとめてあります。

exepy の仕組み

2.0.0 以降の exepy では、以下のようにして exe を作ることができます。

上記で作られた exe は、一時フォルダにファイルを展開したりすることなく、単一の exe ファイルだけで input.py の処理を実行することができます。

さて、このとき、 sample.exe は python.exe とほぼ同じバイナリになっています。
唯一の違いは、 input.py に相当する内容が sample.exe のリソースとして組み込まれているところです。

embeddedimport は、実行している exe ファイル (sample.exe) のリソースに特定の ID のリソースが含まれている場合、その内容から *.py のデータをデコードしロードします。
つまり、 import input が実行された場合、 embeddedimport は以下の順で *.py を探します。

  1. 現在実行中の exe ファイルのリソース内
  2. exe 内に静的に持っている *.py のデータ (具体的には embeddedimport_data.c の内容)
そのため、元々の python.exe にリソースを埋め込むだけで、 *.py を含んだ exe ファイルを作ることができます。

exepy のサンプル

いくつかサンプルを示しておきます。

複数ファイルからなるサンプル

main.py と sub.py があり、 main.py が sub.py を import しているとします。

上記のファイルを下記のように exe 化します。

上記で作られた sample.exe の実行は、実質的に下のコマンドを実行したときとほぼ同等の動きになります。

画像ファイルなどの抱き込み

Single Binary StacklessPython 3.6.6 L16 に含まれる exepy 2.1.0 では、画像ファイルなどの抱き込みができるようになっています。
例えば、 picture.png を tkinter で表示する例を考えます。

以下のような main.py を用意します。

こちらを以下のコマンドで exe 化します。

これで picture.png も組み込まれた exe ができあがります。

__loader__.get_data(...) で、通常のファイルのようにデータを取得することができます。
あとはこれを用いて tkinter の Label, Widget などで表示すればよいです。

今回の変更で embeddedimporter が get_data をサポートしました。
これにより、任意のバイナリデータを組み込めるようになっています。

あとは、アイコンの変更機能ぐらいを追加して開発も終了です。

2018年6月26日火曜日

Single Binary StacklessPython 3.6.4 L8 リリース

Single Binary StacklessPython 3.6.4 L8 リリース

業務の効率化や Python インタプリタの勉強も兼ねて、 StacklessPython を 1 バイナリ化していましたが、バージョン L8 まできました。

GitHub のリリースページからダウンロードできます。

今回は、主に以下の変更をしました。

  • tkinter のサポート
  • EXE 化の実験的サポート
  • Windows Subsystem 版のサポート

tkinter のサポート

これまでに Python の標準ライブラリは、ほぼすべてサポートしていましたが、 tkinter はいくつかの理由でサポートしていませんでした。

tkinter をサポートし、1 バイナリで実行するには、 tcl/tk のライブラリを静的にリンクする必要があります。
また、 tcl/tk は、いくつかの *.tcl ファイルを必要とするので、こちらについても対応する必要があります。

静的リンクについては、 nmake -f makefile.vc core OPTS=static のように OPTS として static を指定すれば、静的リンク版をビルドできるので、こちらを使うことになります。

一方で、*.tcl ファイルの抱き込みについては、やや面倒です。

*.tcl ファイルの抱き込み

tcl では、Tcl_Filesystem という構造体で、複数のファイルシステムを持つことができます。
これは一言で言うと Python の path_hooks に相当するものです。
今回は embeddedFilesystem というファイルシステムを作成し、 embeddedfs:/tcl8.6/init.tcl のように embeddedfs:/ で始まる仮想的なパスが指定された場合に、バイナリの中に保持しているファイルの中身を返すようにしています。

詳しくは tclEmbeddedFilesystem.c を参照してください。

EXE 化の実験的サポート

せっかくなので、おまけの機能として EXE 化の機能を付けてみました。
なお、元々、標準ライブラリ外の機能は考慮していないので、py2exe や PyInstaller の代わりには使えません。あくまで、標準ライブラリだけを使って書けるスクリプトの EXE 化ができます。

例えば下記のように、 tk でウィンドウを作成し、クリックでカウントアップするだけのプログラムを EXE 化してみます。

今回の Single Binary StacklessPython では、上記のように tkinter を使うような場合も、実行することができます。

これを、単一の EXE にするには、下記のようにします。
ただし、これはまだ実験的な機能なので、将来は仕様を変更するかもしれません。

上記を実行すると sample.exe が生成されます。

サイズは 14MB ぐらいになりますが、テンポラリディレクトリにファイルを展開したりはしないので、かなり高速に実行されます。

Windows Subsystem 版のサポート

今回から pythonw.exe のように、コンソール画面が表示されないバイナリを作っておきました。

こちらは、主に GUI を使うスクリプトを EXE 化する際に使うと便利です。

Python, tcl/tk の実装の勉強にもなり、 AppVeyor の利用の勉強にもなりましたが、すべての標準ライブラリを 1 バイナリに収めたので、そろそろ終了にする予定です。

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 のサイトを作った際の技術的な内容について、まとめる予定です。