PyPy’s ctypes implementation

サマリー

専門用語の定義:

  • アプリケーションレベルコード - すべて Python で書かれたコード
  • インタプリタレベルコード - RPython で書かれ、インタプリタを C 等に変換したもの

現状、PyPy における ctypes によるモジュールの実装は CPython における ctypes と同様のインターフェスとふるまいを保証しています。

PyPy の実装では、 CPython における ctypes の libffi のようなものを内部的に利用します。 実装は RPython ではなく、すべて Python で書かれています。 CPython では、同様のものが C で書かれています。 私たちは基本的に実装の早さよりも、動作の速さを重視しています。 実際 ctypes のほとんどの機能の実装に 2 ヶ月間をかけています。

CPython 同様に ctypes のバージョン 1.0.2 を再利用しています。 CPython の C モジュールではなく、 ピュア Python ベースの低レベルのレイヤの拡張モジュール _rawffi_ctypes を実装しています。

低レベル部品: _rawffi

この PyPy 拡張モジュール (pypy/module/_rawffi) は C オブジェクト (配列や構造体) の作成や、 libffi 経由で動的ライブラリの関数を呼び出すためのシンプルなインターフェースを提供します。 大抵のオブジェクトの開放の場合、 互いを参照しているオブジェクトの生存確認はより高レベルで解決します。

このモジュールは pypy/rlib/libffi.py に定義されている libffi バインディングを利用します。

このモジュールはできるだけコンパクトなままにしておくつもりです。 他の実装の場合 (例えば Jython) でも、それぞれのバージョンの _rawffi を書くことにより、 ctypes 実装を利用できると考えています。

高レベル部品

再利用している ctypeslib_pypy/ctypes にあります。 CPython の _ctypes 同じインターフェースの実装の _ctypeslib_pypy/_ctypes にあります。

考察と制限

ctypes の機能のほとんどは再実装可能でした。 PyPy は拡張可能なガーベージコレクタをサポートしており、 そのいくつかのコレクタは動いており、 これは Python オブジェクトの内部で直接参照している外部のライブラリの状態の解析ができないことを意味しています (GC のサポートを限定していない場合は、このケースは当てはまりません)。 その結果、いくつかのケースではいくつかの動作の違いがあるため、共有せずに複製する必要がでてきます。 _rawffi によて作成された C オブジェクトは GC のヒープ外に割り当てられ、 とくになにも気にすることなく外部関数に引き継ぐことができます。

実装のインタプリタレベルのポーティングによりその速度は改善されます。 また、現状のレイヤリングと現状の _rawffi インターフェースは、 さらに厳格なオブジェクトの配置と複製を必要とします; これは改善することができました。

現状の実装における制限と実装されてない機能は以下のようなものがあります。

  • libpythonPyXxx 関数は明確な理由からサポートされていません。
  • 生バッファへのポインタの保持ではなく Python の文字列を複製しています
  • 実装していない機能
    • カスタムアラインメントとビットフィールド
    • リサイズ (resize() 関数)
    • 非ネィティブバイトオーダオブジェクト
    • By-value オブジェクトへのコールバックの許可
    • ctypes のプリミティブ型とユーザサブクラスのプリミティブ型にはいくつかの動作の違いがあります

アプリケーションの実装例

pyglet の実装が知られています。 pygame-ctypes (しばらくメンテナンスされていません) や pysqlite-ctypes のスナップショット等の成功例があります。 pyglet の実装方法を例示します。

pyglet

pyglet のリポジトリからリビジョン 1984 をチェックアウトします。

pyglet では以下の動作例が知られています:

  • opengl.py
  • multiple_windows.py
  • events.py
  • html_label.py
  • timer.py
  • window_platform_event.py
  • fixed_resolution.py

ctypes のテストを実行する pypy-c は、同様に pyglet の例の実行に用いることができます。

$ cd pyglet/ $ PYTHONPATH=. ../ctypes-stable/pypy/translator/goal/pypy-c examples/opengl.py

通常 ctrl-c で終了することができます。

以下の例は ctypes から独立しているため動作しません。

  • image_convert.py needs PIL
  • image_display.py needs PIL
  • astraea/astraea.py needs PIL

以下の例は試していません:

  • media_player.py は avbin または、少なくとも .wav ファイルを再生するために適切なサウンドカードのセットアップが必要です
  • video.py は avbin が必要です
  • soundscape は avbin が必要です

ctypes のコンフィグレーション

ctypes ベースのコードの移植性の課題を解決するための実験的なパッケージとして ctypes-configure もリリースしています。

アイデア

ctypes の問題点のひとつは、通常 ctypes プログラムにプラットフォーム依存があることです。 各プラットフォームに依存する型のサイズ (例えば size_t)、 #defines や構造体のアウトライン等の細部のために C コンパイラを呼び出す (distutils 経由) ctypes_configure を作成し、

インストール

easy_install ctypes_configure

利用方法

ctypes_configure/doc/sample.py で詳細な利用方法について解説しています。