Python類のファイルオブジェクトをC言語から読み出す


問題
任意のPythonファイルのオブジェクトからデータを読み出すためにC拡張を書きます。
ソリューション
クラスのファイルオブジェクトのデータを読み込むには、read()メソッドを呼び出して、正しい復号で得られたデータを繰り返す必要があります。
次のC拡張関数の例は、1つのクラスのファイルオブジェクトのすべてのデータを読み取り、標準出力に出力するだけです。

#define CHUNK_SIZE 8192

/* Consume a "file-like" object and write bytes to stdout */
static PyObject *py_consume_file(PyObject *self, PyObject *args) {
 PyObject *obj;
 PyObject *read_meth;
 PyObject *result = NULL;
 PyObject *read_args;

 if (!PyArg_ParseTuple(args,"O", &obj)) {
  return NULL;
 }

 /* Get the read method of the passed object */
 if ((read_meth = PyObject_GetAttrString(obj, "read")) == NULL) {
  return NULL;
 }

 /* Build the argument list to read() */
 read_args = Py_BuildValue("(i)", CHUNK_SIZE);
 while (1) {
  PyObject *data;
  PyObject *enc_data;
  char *buf;
  Py_ssize_t len;

  /* Call read() */
  if ((data = PyObject_Call(read_meth, read_args, NULL)) == NULL) {
   goto final;
  }

  /* Check for EOF */
  if (PySequence_Length(data) == 0) {
   Py_DECREF(data);
   break;
  }

  /* Encode Unicode as Bytes for C */
  if ((enc_data=PyUnicode_AsEncodedString(data,"utf-8","strict"))==NULL) {
   Py_DECREF(data);
   goto final;
  }

  /* Extract underlying buffer data */
  PyBytes_AsStringAndSize(enc_data, &buf, &len);

  /* Write to stdout (replace with something more useful) */
  write(1, buf, len);

  /* Cleanup */
  Py_DECREF(enc_data);
  Py_DECREF(data);
 }
 result = Py_BuildValue("");

 final:
 /* Cleanup */
 Py_DECREF(read_meth);
 Py_DECREF(read_args);
 return result;
}
このコードをテストするには、StringIOの例のようなクラスのファイルオブジェクトを作成し、次のように伝えます。

>>> import io
>>> f = io.StringIO('Hello
World
') >>> import sample >>> sample.consume_file(f) Hello World >>>
討論する
通常のシステムファイルとは違って、クラスのファイルオブジェクトは、低レベルのファイル記述子を使用して構築する必要がありません。したがって、通常のCライブラリ関数を使ってアクセスすることはできません。PythonのC APIを使って、普通のファイルのように種類のファイルのオブジェクトを操作する必要があります。
我々の解決策では、read() 方法は、転送されたオブジェクトから抽出される。一つのパラメータリストを構築し、PyObject_Call() に伝えて、この方法を起動する。ファイルの末尾(EOF)を確認するには、PySequence_Length() を使用して、オブジェクトの長さが0に戻るかどうかを確認します。
すべてのI/O操作については、下のコードフォーマットに注目し、バイトとユニック前の違いもあります。このセクションでは、テキストモードでファイルを読み込み、結果テキストをバイトコードに復号する方法を示します。これはCで使えます。バイナリモードでファイルを読みたいなら、少しだけ修正すればいいです。例えば、

...
/* Call read() */
if ((data = PyObject_Call(read_meth, read_args, NULL)) == NULL) {
 goto final;
}

/* Check for EOF */
if (PySequence_Length(data) == 0) {
 Py_DECREF(data);
 break;
}
if (!PyBytes_Check(data)) {
 Py_DECREF(data);
 PyErr_SetString(PyExc_IOError, "File must be in binary mode");
 goto final;
}

/* Extract underlying buffer data */
PyBytes_AsStringAndSize(data, &buf, &len);
...
このセクションで一番難しいのは、どのように正しいメモリ管理を行うかです。 PyObject *変数を処理する際には、参照カウントの管理と不要な変数の整理が必要です。Py_DECREF()への呼び出しはこれです。
このセクションのコードは汎用的に作成されていますので、彼はファイルを書くなど、他のファイル操作にも適用されます。例えば、データを書くには、クラスのファイルオブジェクトを取得するwrite()方法だけが必要であり、適切なPythonオブジェクト(バイトまたはUnicode)にデータを変換し、この方法を呼び出して入力をファイルに書き込む。
最後に、クラスファイルオブジェクトは通常、readline(readline)、read_uなどの他の方法も提供される。info()は基本的なread() write() の方法だけを使用したほうがいいです。C拡張を書くときは、簡単にできるだけ簡単に書くことができます。
以上はC言語からPython類のファイルオブジェクトの詳細を読み取りました。C言語でPython類のファイルを読み込む資料については他の関連記事に注目してください。