CPythonでmain()から>>>が表示されるまでのツアーを駆け抜けてみる

**##編集途中##**
#概要
CPythonの動作を理解するための準備として、ツアー[2]のChapter 1に部分的に参加してみる。
main()から\>\>\>が表示されるまで。

この投稿は、[2]のいびつな抜粋和訳になることをお許しくださいm(_ _)m

#目的
CPythonの内部構造を理解するための準備として、main関数の挙動を確認するため。

#環境
os: maxOS Sierra Version 10.12.6

#内容

#詳細
##レポジトリのダウンロード
今回は、事前にダウンロード済みだが、以下のようにターミナルで実行すればいいはずである。[1]

```bash:ターミナル
$ git clone https://github.com/python/cpython.git
```

##レポジトリのブランチの変更
ツアーで用いるソースコードはPython 3.5のものであるため、gitのブランチを3.5に変更した。

```bash:ターミナル
$ 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()から実行が開始される。

```c:Programs/python.c（部分）
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
```c: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
```c: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
```c: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
```c: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
```c: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の格納をみるため）
```c:Parser/parsetok.c（部分）
/* 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
```c: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
```c: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
```c: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
```c: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のタイプミスだと思われる。

```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
```c: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)