Python 3.0 Hacks

第9回 Python3にもC拡張モジュールを─Python3.0でも使える拡張モジュール開発手法の確立

この記事を読むのに必要な時間:およそ 3 分

目的

Python3.0に限らず,新しいバージョンのPythonがリリースされるたびに悩ましい問題があります。バイナリ製の拡張モジュールが豊富である所がPythonのひとつのウリなんですが,Pythonのメジャーバージョンをまたいでは動作しないのがツライところ。特にWindowsではコンパイラが標準でインストールされていないこともあり,他のOSほどソースからのビルドが容易ではありません。バイナリがリリースするまで待つことになると,拡張モジュールの新しいバージョンが出揃うのにどうしても時間がかかってしまうのです。これが,Pythonのニューバージョンの普及が出遅れる要因のひとつになっています。

この問題の主な要因に,ライブラリとPythonの「橋渡し部分」「C/C++の記述力」を利用して構築している事が挙げられます。ここの「橋渡し部分」のコンパイル作業によりPythonランタイムとの依存関係が発生しますので,「Pythonランタイムのバージョンアップ=リビルドが必要」になります。この「橋渡し部分」をPythonコードだけで作る方法を用いると,その手法で作ったモジュールはPythonのメジャーバージョンの違いに関係なく動作可能になります。最近のPythonはその橋渡しをPythonで記述できる「ctypes」というモジュールを標準で添付しています。また,ctypesベースの拡張モジュールもどんどん作られて増えてきています。

しかしこの「ctypes」は,C言語の型情報を考慮してctypes流儀の記述を自前で用意しなくてはいけないのが面倒なんです。Pythonの基本型とCの基本型の共通のものだけは自動的に変換してくれますが,その他の定数,列挙型,構造体,別名型定義などは無理です。開発中のctypeslibというツールセットを使うと,ctypes流儀のPythonコードを自動生成できます。

本記事では,C言語で作られたダイナミックリンクライブラリとヘッダから橋渡し部分のPythonモジュールを自動生成し,そのモジュールがPython3.0で動作することを示します。

仕組み

この手法では,まずC言語でダイナミックリンクライブラリを作成しておき,ctypeslibパッケージのh2xmlツールでgccxmlツールを経由して,Cヘッダを解析したXMLファイルを作成します。gccxmlの実装はgccにパッチを当てて,C/C++解析結果をXMLに出力できるようにしたものなので,そのXMLファイルには構文解析によって得られる詳細な情報が詰まっています。

次にctypeslibパッケージのxml2pyツールを利用してそのXMLファイルからPythonのコードだけでできた「橋渡し部分」つまり「ラッパーモジュール」を生成します。「ラッパーモジュール」はヘッダにある定義群(定数,列挙,関数,クラス,構造体,型宣言)をPythonのctypesに従った記述に置き換えたもので,普通ならctypes用のコードを書いて準備を手作業でやらねばならなかったところを,上記手法なら自動的に記述を生成できます。そのモジュールはPython2.5や2.6で動作するモジュールになっていますが,これを「Python3.0」付属の「2to3.py」ツールにてPython3.0用に変換することで「Python3.0」で動作可能なモジュールを構築できます。

利用するにはダイナミックリンクライブラリを環境変数PATHの通る場所に置いて,Pythonのモジュールをインポートするだけです。あとは,ヘッダで定義した定数や構造体を使って関数呼び出しも自在にできるようになっています。ヘッダに変更が無ければ,ダイナミックリンクライブラリのバージョン更新にも影響なく動作可能でなおかつctypesモジュールを持つPythonならどのバージョンでも動きます。

ctypeslibツールセットの準備

あらかじめ,gccxml-0.9.0をインストールしておきます。

上記サイトから gccxml-0.9.0-win32-x86.exe をダウンロード。バイナリインストーラを起動すれば簡単にインストールできます。次にctypeslibツールセットをインストールします。ソースからインストールしたい方以外は,以下のセクションはスキップしてください。

ctypeslibパッケージのインストール

以下のサイトからctypeslibパッケージを入手します。

svnか,TortoiseSVNでチェックアウトしましょう。ODEライブラリのために少しだけ修正を加えます(ODEを利用しない方はこの修正は無用です)。

svnの場合

svn co http://svn.python.org/projects/ctypes/branches/ctypeslib-gccxml-0.9/ ctypeslib

その後以下の修正を行います。

fix(gccxmlparser.py line.112 add):
    def Converter(self, attrs): pass

fix(codegenerator.py line.58 change):
    "long double": "c_double",

その上で

ctypeslib> python setup.py install

とすると,「Pythonホーム/Scripts」フォルダにh2xml.pyとxml2py.pyがインストールされます。これらを利用することで,C言語ヘッダとダイナミックリンクライブラリからPythonモジュールを生成できるようになります。 しかし,このツール私の力不足でPython3ではうまく動きませんでした。

バイナリアーカイブの公開

上記セクションの処理を済ませたPython2.5バージョンのバイナリを配布します。

このアーカイブを適当なところに解凍して,そのフォルダパスを環境変数のPathに加えておいてください。

実際の手順

シンプルな利用例

h2xml windows.h -o windows.xml
xml2py windows.xml -w -s Beep -o beep.py

「-w」は実体をWindowsの標準DLLから検索するモードになります。標準でないDLLを利用する場合は「-w」の代わりに「-l hoge.dll」と名指しします。「-r」「-s」を省略すると,可能な限りのシンボルをコンバートします。「-s」は単独のシンボルを指定できます。「-r」は取り込むシンボルを正規表現で指定できます(「-r」「-s」は複数指定できます)。

beep.pyのテスト

>>> import beep
>>> beep.Beep(440, 500)
1

このようにWindowsAPIを呼び出すモジュールが簡単に作れます。Python3では文字列はUnicodeなので,末尾がAかWのAPIを利用する場合,WつきのAPIを利用することになります。

リスト1のバッチファイルを実行すると,WindowsのAPIがほとんど利用可能なモジュール「windows.py」ができます。

リスト1 build.bat

REM PYTHON3にはPython3.0をインストールしたフォルダを指定してください。
set PYTHON3=C:\Python30
set PY2TO3=%PYTHON3%\python %PYTHON3%\Tools\Scripts\2to3.py
set PYTHONPATH=

h2xml windows.h -DWIN32_LEAN_AND_MEAN -DUNICODE -c -q -o windows.xml
xml2py windows.xml -w -o windows.py
%PY2TO3% -w windows.py

windows.pyのテスト

>>> from windows import *
>>> MessageBox(0, 'メッセージ', 'タイトル', 0)
1

図1 テスト結果

図1 テスト結果

著者プロフィール

入江田昇(いりえだ のぼる)

メカ整備やエレクトロニクス系の業務が長く,ロボットに関わることが多い。国のRTミドルウェアプロジェクトでPythonを強く推した張本人。Python/C++を中心に制御ソフトや業務支援ツールなどをよく作成している。自分でブログシステムを構築し,Pythonに関するサイトを運営しながらWeb系の勉強中。最近はFPGAロジックをPythonで書くのが楽しい。

コメント

コメントの記入