PyCodeObjectを書き換える
Pythonのcodeオブジェクトを書き換えてしまう拡張ライブラリ「CodeHack」を作りました。
下の使用例では、関数の中の加算命令を乗算命令に書き換えています。
import CodeHack
def f(x):
print x + 2
f(10) #-> 12
# replace ADD to MULTIPLY
code = f.func_code.co_code
code = code.replace("\x17", "\x14")
CodeHack.set_co_code(f.func_code, code)
f(10) #-> 20
Pythonの拡張ライブラリを作って公開するのも初めてですし、 C言語をさわるのも数年ぶりなのですが、 想像以上に簡単でした。 全部distutilsがやってくれるので、 コンパイラを自分で呼ぶ必要すらありませんでした。 see Python モジュールの配布。
ダウンロード
(22:31にバージョン0.02を公開しました。0.01はたまにPythonが異常終了します。)CodeHack-0.0.2.win32.exe(Windows用インストーラ)
CodeHack-0.02.zip(ソースコード)
原理
原理はとても簡単(原理と言うほどでもないくらい簡単)です。 コードオブジェクトのco_code属性はリードオンリーフラグが立っているためにPythonのプログラムで直接書き換えることはできませんが、 Cのコードから書き換えることは可能です。 そこで、コードオブジェクトと文字列を受け取って、co_codeを書き換えてしまうモジュールをCで書きました。
#include <Python.h>
static PyObject *
codehack(PyObject *self, PyObject *args)
{
PyObject* c; // PyCodeObject
PyObject* s; // PyString
if (!PyArg_ParseTuple(args, "OO", &c, &s))
return NULL;
PyCodeObject* code = (PyCodeObject*) c;
Py_INCREF(s);
code->co_code = s;
Py_RETURN_NONE;
}
static PyMethodDef Methods[] = {
{"set_co_code", codehack, METH_VARARGS, "overwrite co_code"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initCodeHack(void)
{
(void) Py_InitModule("CodeHack", Methods);
}