##編集途中##
概要
CPythonの動作を理解するための準備として、ツアー[2]のChapter 1に部分的に参加してみる。
main()から>>>が表示されるまで。
この投稿は、[2]のいびつな抜粋和訳になることをお許しくださいm(_ _)m
目的
CPythonの内部構造を理解するための準備として、main関数の挙動を確認するため。
環境
os: maxOS Sierra Version 10.12.6
内容
詳細
レポジトリのダウンロード
今回は、事前にダウンロード済みだが、以下のようにターミナルで実行すればいいはずである。[1]
$ git clone https://github.com/python/cpython.git
レポジトリのブランチの変更
ツアーで用いるソースコードはPython 3.5のものであるため、gitのブランチを3.5に変更した。
$ pwd
/Users/なんやらかんやら/cpython
$ git branch -a
* master
remotes/origin/2.7
remotes/origin/3.4
remotes/origin/3.5
remotes/origin/3.6
remotes/origin/HEAD -> origin/master
remotes/origin/master
$ git checkout 3.5
Checking out files: 100% (2624/2624), done.
Branch 3.5 set up to track remote branch 3.5 from origin.
Switched to a new branch '3.5'
$ git branch
* 3.5
master
ブランチが、3.5に変更された。
main() in Programs/python.c
全てのC言語は、main()から実行が開始される。
int
main(int argc, char **argv)
{
wchar_t **argv_copy;
/* We need a second copy, as Python might modify the first one. */
wchar_t **argv_copy2;
int i, res;
char *oldloc;
(中略)
res = Py_Main(argc, argv_copy);
(中略)
return res;
}
Py_Main()が呼ばれていることが分かった。
The thing to note is that the code here just ends up calling Py_Main() , which is defined in Modules/main.c . On to that file! (PS. There’s an old historical reason why it lives in Modules , not in Python . But it’s not important here.)
Py_Main() in Modules/main.c
/* Main program */
int
Py_Main(int argc, wchar_t **argv)
{
(中略:オプション解釈など)
Py_Initialize();
(中略)
if ((Py_InspectFlag || (command == NULL && filename == NULL && module == NULL)) &&
isatty(fileno(stdin))) {
PyObject *v;
v = PyImport_ImportModule("readline");
if (v == NULL)
PyErr_Clear();
else
Py_DECREF(v);
}
(中略)
if (Py_InspectFlag && stdin_is_interactive &&
(filename != NULL || command != NULL || module != NULL)) {
Py_InspectFlag = 0;
RunInteractiveHook();
/* XXX */
sts = PyRun_AnyFileFlags(stdin, "<stdin>", &cf) != 0;
}
Py_Finalize();
(中略)
return sts;
}
(Py_Initialize()は、インタープリタを開始し、あらゆるオブジェクトを使用可能にする。
The key thing is that once Py_Initialize() has run, the interpreter is open for business, and we can use all kinds of objects.
v = PyImport_ImportModule("readline");
では、インタラクティブなコマンドラインを、Pythonモジュール"readline"
としてimportしようとしている。
After this there’s some code that tries to import the readline module, which provides interactive command line editing at the >>> prompt. Sometimes this can’t be imported, and we clear the exception in that case. This is a pretty standard pattern and good to understand:
副作用のみに関心があるから、今回の駆け抜けではそこまで気にしなくて良い?)
PyRun_AnyFileFlags()が呼ばれていることが分かった。
This function lives in Python/pythonrun.c and is a wrapper that calls either PyRun_InteractiveLoopFlags() or PyRun_SimpleFileExFlags() , both defined in the same file. And this is what I’ve promised: the code that prints the >>> prompt lives in the former.
PyRun_AnyFileFlags()(,PyRun_InteractiveLoopFlags()) in Python/pythonrun.c
int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
PyCompilerFlags *flags)
{
if (filename == NULL)
filename = "???";
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);
}
int
PyRun_InteractiveLoopFlags(FILE *fp, const char *filename_str, PyCompilerFlags *flags)
{
(中略)
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);
_PY_DEBUG_PRINT_TOTAL_REFS();
if (ret == E_EOF) {
err = 0;
break;
}
/*
if (ret == E_NOMEM)
break;
*/
}
Py_DECREF(filename);
return err;
}
PyRun_InteractiveOneObject()が呼ばれていることが分かった。
PyRun_InteractiveOneObject() in Python/pythonrun.c
int
PyRun_InteractiveOneObject(FILE *fp, PyObject *filename, PyCompilerFlags *flags)
{
(中略)
v = _PySys_GetObjectId(&PyId_ps1);
(中略)
ps1 = _PyUnicode_AsString(v);
(中略)
w = _PySys_GetObjectId(&PyId_ps2);
(中略)
ps2 = _PyUnicode_AsString(w);
(中略)
mod = PyParser_ASTFromFileObject(fp, filename, enc,
Py_single_input, ps1, ps2,
flags, &errcode, arena);
(中略)
v = run_mod(mod, filename, d, d, flags, arena);
(中略)
return 0;
}
PyParser_ASTFromFileObject()が呼ばれていることが分かった。
PyParser_ASTFromFileObject() in Python/pythonrun.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;
PyCompilerFlags localflags;
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);
}
(中略)
return mod;
}
PyParser_ParseFileObject()が呼ばれていることが分かった。
PyParser_ParseFileObject() in Parser/parsetok.c
node *
PyParser_ParseFileObject(FILE *fp, PyObject *filename,
const char *enc, grammar *g, int start,
const char *ps1, const char *ps2,
perrdetail *err_ret, int *flags)
{
struct tok_state *tok;
(中略)
if ((tok = PyTokenizer_FromFile(fp, enc, ps1, ps2)) == NULL) {
err_ret->error = E_NOMEM;
return NULL;
}
(中略)
return parsetok(tok, g, start, err_ret, flags);
}
PyTokenizer_FromFile()でプロンプトがtok
にセットされ、
parsetok()が呼ばれていることが分かった。
PyTokenizer_FromFile() in Parser/parsetok.c (ps1, ps2の格納をみるため)
/* Set up tokenizer for file */
struct tok_state *
PyTokenizer_FromFile(FILE *fp, const char* enc,
const char *ps1, const char *ps2)
{
struct tok_state *tok = tok_new();
(中略)
tok->prompt = ps1;
tok->nextprompt = ps2;
(中略)
return tok;
}
parsetok() in Parser/parsetok.c
/* Parse input coming from the given tokenizer structure.
Return error code. */
static node *
parsetok(struct tok_state *tok, grammar *g, int start, perrdetail *err_ret,
int *flags)
{
parser_state *ps;
node *n;
int started = 0;
(中略)
for (;;) {
char *a, *b;
int type;
size_t len;
char *str;
int col_offset;
type = PyTokenizer_Get(tok, &a, &b);
(中略)
if ((err_ret->error =
PyParser_AddToken(ps, (int)type, str,
tok->lineno, col_offset,
&(err_ret->expected))) != E_OK) {
if (err_ret->error != E_DONE) {
PyObject_FREE(str);
err_ret->token = type;
}
break;
}
}
if (err_ret->error == E_DONE) {
n = ps->p_tree;
ps->p_tree = NULL;
(中略)
}
(中略)
return n;
}
PyTokenizer_Get()が呼ばれていることが分かった。
PyTokenizer_Get() in Parser/tokenizer.c
int
PyTokenizer_Get(struct tok_state *tok, char **p_start, char **p_end)
{
int result = tok_get(tok, p_start, p_end);
if (tok->decoding_erred) {
result = ERRORTOKEN;
tok->done = E_DECODE;
}
return result;
}
tok_get()が呼ばれていることが分かった。
tok_get() in Parser/tokenizer.c
/* Get next token, after space stripping etc. */
static int
tok_get(struct tok_state *tok, char **p_start, char **p_end)
{
int c;
int blankline, nonascii;
(中略)
/* Get indentation level */
if (tok->atbol) {
int col = 0;
int altcol = 0;
tok->atbol = 0;
for (;;) {
c = tok_nextc(tok);
(中略)
return PyToken_OneChar(c);
}
tok_nextc()が呼ばれていることが分かった。
tok_nextc() in Parser/tokenizer.c
/* Get next char, updating state; error code goes into tok->done */
static int
tok_nextc(struct tok_state *tok)
{
for (;;) {
if (tok->cur != tok->inp) {
return Py_CHARMASK(*tok->cur++); /* Fast path */
}
(中略)
if (tok->prompt != NULL) {
char *newtok = PyOS_Readline(stdin, stdout, tok->prompt);
PyOS_Readline()が呼ばれていることが分かった。
PyOS_Readline() in Parser/myreadline.c
原文では、Python/myreadline.cにあるとなっているが、Parser/myreadline.cのタイプミスだと思われる。
/* Interface used by tokenizer.c and bltinmodule.c */
char *
PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
{
char *rv, *res;
size_t len;
(中略)
if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
(中略)
len = strlen(rv) + 1;
res = PyMem_Malloc(len);
if (res != NULL)
memcpy(res, rv, len);
PyMem_RawFree(rv);
return res;
}
PyOS_StdioReeadline()が呼ばれていることが分かった。
PyOS_StdioReeadline() in Parser/myreadline.c
/* Readline implementation using fgets() */
char *
PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
{
(中略)
fflush(sys_stdout);
if (prompt)
fprintf(stderr, "%s", prompt);
fflush(stderr);
(中略)
switch (my_fgets(p, (int)n, sys_stdin)) {
(中略)
}
(中略)
return pr;
}
fprintf
!!
参考文献
[1] python/cpython(https://github.com/python/cpython)
[2]Yet another guided tour of CPython(https://paper.dropbox.com/doc/Yet-another-guided-tour-of-CPython-XY7KgFGn88zMNivGJ4Jzv)