実践Python拡張モジュールづくり (UNIX編)

 このページではUNIX環境でC言語を使ってPythonの拡張モジュールを作る方法を手順を追って紹介してみます。 コンパイラはgcc、Python 2.0で確認しています。

ソースプログラムを書く
 まずはソースプログラムを書きます。 前のページの例はあまりに簡単すぎて意味のないものだったので、 多少は「役に立つ」ものを作ってみましょうか?

#include "Python.h"
#include <ctype.h>
#include <string.h>

/* 引数は
 * escaper.escape("string")
 * escaper.escape("string", 進数(8 or 16)
 * の2通りを可能とします。上の場合は16進数モードになります。
 */
static PyObject* escaper_escape(PyObject* self, PyObject* args){
  int radi;
  int i;
  int srclen;
  char* srcp;
  PyObject* outobj;

  /* 引数のデータ型「s#」は文字列本体とその長さの組を表します */
  if(! PyArg_ParseTuple(args, "s#i", & srcp, & srclen, & radi)){
    if(! PyArg_ParseTuple(args, "s#", & srcp, & srclen))
      return NULL;
    else
      radi = 16;
  }
  if(radi != 8 && radi != 16){
    PyErr_SetString(PyExc_AttributeError,
                    "radix parameter must be one of: 8, 16.");
    return NULL;
  }

  {
    char* p, * q, * resultp;
    int i;

    /* 元の文字列の最大4倍の長さになる可能性があります */
    if((resultp = (char* )malloc(srclen * 4)) == NULL){
      PyErr_SetString(PyExc_MemoryError, "Ootto! out of memory.");
      return NULL;
    }

    for(i=0, p=srcp, q=resultp; i<srclen; i++){
      if(isprint(*p))
        *q++ = *p++;
      else{
        char tbuf[10], * r;
        if(radi == 16){
          sprintf(tbuf, "%02x", *p); *q++ = '\\'; *q++ = 'x';
        }
        else if(radi == 8){
          sprintf(tbuf, "%03o", *p); *q++ = '\\';
        }
        for(r=tbuf; *r; *q++ = *r++) ;
        p++;
      }
    }
    *q = '\0';

    outobj = Py_BuildValue("s", resultp);
    free( (char* ) resultp );
    return outobj;
  }
}

static PyMethodDef defs[] = {
  {"escape", escaper_escape, METH_VARARGS},
  {NULL, NULL}
};

void initescaper(void){
  Py_InitModule("escaper", defs);
}
/* end. */

 このソースプログラムをコンパイルすると、 「escaper」というPythonモジュールを作ることができます。 そのescaperには「escape」という関数が1個含まれ、 escaper.escapeは渡された文字列に含まれる制御文字(非表示文字) を\x03などのエスケープ表記に変換して返します。8進か16進で選択できるので、

import escaper
a = escaper.escape('\x0a\x00\x75')
b = escaper.escape('\x0a\x00\x75', 8)

のように使うことができます。
 ソースプログラムの説明は先述の通りです。引数の受け取り方、 エラー処理の仕方、戻り値の返し方それぞれ、 決まったPython APIを使えばいいので分かりやすいと思います。

void initescaper(void){
  Py_InitModule("escaper", defs);
}
 モジュールには固有のモジュール名が必要ですが、 それはこの初期化関数でPy_InitModuleを使って定義します。 ここではモジュールの名前を「escaper」にしていますが、 初期化関数の名前は、 必ず「init」+モジュール名に合わせないといけないことに注意が必要です。 またソースプログラムのファイル名は「escapermodule.c」としておきます。
 UNIXでPythonモジュールをビルドするには、 Setupファイルを書くのが簡単な方法です。 下のようなテキストファイルを書き、「Setup」という名前で保存します。
*shared*
escaper escapermodule.o
# end.
そして、Pythonのソース配布のMiscディレクトリから、「Makefile.pre.in」 というファイルをコピーしてきます。 Makefile.pre.in、Setup、それにescapermodule.cの3つがそろったら、 次の手順でビルドします。
% make -f Makefile.pre.in boot
% make
escapermodule.so (HP-UXでは.sl)という共有ライブラリが無事にできれば大成功です。 このライブラリ名は固定です。つまり、 モジュール名+「module.so」(または.sl) という名前でなければいけません。
 できた共有ライブラリは、Pythonのモジュールパス、 通常は /usr/local/lib/python2.0/site-dynload に置きます。どうしてもrootになれない場合は、どこか好きな場所に置き、 環境変数PYTHONPATHをそこに通せばOKです。

セクションのサブメニューに戻る
(first uploaded 2001/01/27 last updated 2002/07/20)