[[Pythonを読む]] #contents *Py_Main (Modules/main.c) [#l307a9ec] 読んだことないので先頭から。main関数はPrograms/python.cにあります。argvがwchar_t**じゃないときは前処理が行われていますがさくっと無視して本体らしいPy_Main関数に移ります。こちらはディレクトリが変わってModules/main.cにあります。 Py_Main関数は500行ぐらいありますが、初めの300行ぐらいはコマンドラインの処理をしていますが無視します。 コマンドライン処理が終わったぐらいのところでPy_Initialize関数(Python/pylifecycle.c)を呼び出しています。この関数(から呼ばれている_Py_InitializeEx_Private)でインタプリタやらスレッドやらオブジェクトシステムやらを初期化していますがまあ呼ばれている関数は名前の通りの動作をしているのだろうということでその先を深追いはしません。 その後、コマンド指定(-c)がされているか、モジュール指定(-m)がされているか、ファイルが指定されているかによって処理が行われていますが今回は何も指定していないので、ファイルオープンなしのままrun_fileが呼ばれることになります。余計な部分を削ると、 #code(C){{ run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf) { char *filename_str; int run; if (filename) { // 省略 } else filename_str = "<stdin>"; run = PyRun_AnyFileExFlags(fp, filename_str, filename != NULL, p_cf); return run != 0; } }} fpはstdinです。 * PyRun_InteractiveOneObject (Python/pythonrun.c) [#ce2840a8] PyRun_AnyFileExFlags。入力がインタラクティブの時はInteractiveLoopに入って期待通りです。 #code(C){{ int PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags) { if (Py_FdIsInteractive(fp, filename)) { int err = PyRun_InteractiveLoopFlags(fp, filename, flags); if (closeit) fclose(fp); return err; } else return PyRun_SimpleFileExFlags(fp, filename, closeit, flags); } }} PyRun_InteractiveLoopFlagsに進みます。エラー処理とかを省くと以下のような感じ。 #code(C){{ int PyRun_InteractiveLoopFlags(FILE *fp, const char *filename_str, PyCompilerFlags *flags) { PyObject *filename, *v; int ret, err; filename = PyUnicode_DecodeFSDefault(filename_str); v = _PySys_GetObjectId(&PyId_ps1); if (v == NULL) { _PySys_SetObjectId(&PyId_ps1, v = PyUnicode_FromString(">>> ")); Py_XDECREF(v); } v = _PySys_GetObjectId(&PyId_ps2); if (v == NULL) { _PySys_SetObjectId(&PyId_ps2, v = PyUnicode_FromString("... ")); Py_XDECREF(v); } err = -1; for (;;) { ret = PyRun_InteractiveOneObject(fp, filename, flags); if (ret == E_EOF) { err = 0; break; } } Py_DECREF(filename); return err; } }} PyRun_InteractiveOneObjectと、シェルに入力した式が実行される雰囲気が漂います。長いのでざっくり必要な部分だけ残すと以下のような感じ。 #code(C){{ int PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags) { PyObject *m, *d, *v, *w, *oenc = NULL, *mod_name; mod_ty mod; PyArena *arena; char *ps1 = "", *ps2 = "", *enc = NULL; int errcode = 0; _Py_IDENTIFIER(encoding); _Py_IDENTIFIER(__main__); mod_name = _PyUnicode_FromId(&PyId___main__); /* borrowed */ if (fp == stdin) { /* Fetch encoding from sys.stdin if possible. */ v = _PySys_GetObjectId(&PyId_stdin); if (v && v != Py_None) { oenc = _PyObject_GetAttrId(v, &PyId_encoding); if (oenc) enc = _PyUnicode_AsString(oenc); } } arena = PyArena_New(); mod = PyParser_ASTFromFileObject(fp, filename, enc, Py_single_input, ps1, ps2, flags, &errcode, arena); Py_XDECREF(v); Py_XDECREF(w); Py_XDECREF(oenc); m = PyImport_AddModuleObject(mod_name); d = PyModule_GetDict(m); v = run_mod(mod, filename, d, d, flags, arena); PyArena_Free(arena); Py_DECREF(v); flush_io(); return 0; } }} 入力からASTを作り、実行しています。PyParser_ASTFromFileObjectはpythonrun.cの下の方にあります。 #code(C){{ mod_ty PyParser_ASTFromFileObject(FILE *fp, PyObject *filename, const char* enc, int start, char *ps1, char *ps2, PyCompilerFlags *flags, int *errcode, PyArena *arena) { mod_ty mod; perrdetail err; int iflags = PARSER_FLAGS(flags); node *n = PyParser_ParseFileObject(fp, filename, enc, &_PyParser_Grammar, start, ps1, ps2, &err, &iflags); if (n) { flags->cf_flags |= iflags & PyCF_MASK; mod = PyAST_FromNodeObject(n, flags, filename, arena); PyNode_Free(n); } err_free(&err); return mod; } }} run_mod。 #code(C){{ static PyObject * run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, PyCompilerFlags *flags, PyArena *arena) { PyCodeObject *co; PyObject *v; co = PyAST_CompileObject(mod, filename, flags, -1, arena); v = PyEval_EvalCode((PyObject*)co, globals, locals); Py_DECREF(co); return v; } }} というわけで、教科書のように以下の順番で処理が行われているようです。 +スクリプトからノードへの変換:PyParser_ParseFileObject (Parser/parsetok.c) +ノードからASTへの変換:PyAST_FromNodeObject (Python/ast.c) +ASTからバイトコードへの変換:PyAST_CompileObject (Python/compile.c) +バイトコードの実行:PyEval_EvalCode (Python/ceval.c) スクリプト解析は長くなるので[[別ページ>Python/スクリプト解析を読む]]で書きます。 ここまでの感想、ってほどでもないですが、慣れてないせいかRubyに比べてどこに関数が書かれているかわかりにくくgrepで探して進んでいます。タグジャンプ使えばいい気がしてきたw