CPython Reading 2011/3/27

お前, 誰よ?

  • @cocoatomo

  • B2B の基幹業務パッケージを作る会社
    • 研究開発部門みたいなとこ
    • 普段は Java 使い
  • Python との出会い: 仕事で Jython

  • Python document の翻訳やってます

今日話すこと

  • Python はどのように実行されるのか?
    • Python の main から parse まで.
  • 将来の目的: Python の型まわりをいじってみたい
    • PEP 3107 Functional Annotations

ソースコード読むと何が良いのか?

  • C での OOP 言語の実装が学べる

  • CPython の特性が理解できる

  • マルチプラットフォーム対応の大変さが分かる
    • Java って楽ですね
  • CPython を改造して俺々 Python が作れる

  • 俺々言語の実装の参考に

新しい言語って誰得?

結局は自己満足です. はい.

道具立て

  • gtags

    $ gtags -v
    $ htags
    
  • gtags-mode

    (setq gtags-mode-hook
          '(lambda ()
             (local-set-key "\M-t" 'gtags-find-tag)
             (local-set-key "\M-r" 'gtags-find-rtag)
             (local-set-key "\M-s" 'gtags-find-symbol)
             (local-set-key "\C-t" 'gtags-pop-stack)))
    (add-to-list 'exec-path "/usr/local/bin")
    (setenv "PATH" (concat '"/usr/local/bin:" (getenv "PATH")))
    

This Presentation Powered by...

  • Sphinx
    • ソースコードから HTML へ変換
  • sphinxjp.theme.s6
    • HTML, CSS, js でプレゼン
    • special thanks: @shimizukawa

全体像

  • main
    • Py_Main
      • run_command

      • RunModule

      • RunMainFromImporter

      • run_file
        • PyRun_AnyFileExFlags

全体像つづき

  • PyRun_AnyFileExFlags
    • PyRun_InteractiveLoopFlags

    • PyRun_SimpleFileExFlags
      • run_pyc_file

      • PyRun_FileExFlags
        • PyParser_ASTFromFile
        • run_mod

PyParser_ASTFromFile

  • PyParser_ParseFileFlagsEx
    • PyTokenizer_FromFile
    • PyAST_FromNode

注意

ここからひたすらソースコードが続きます.

各自の手元のソースと照らし合わせながら発表を聞いてください.

Modules/python.c

int
main(int argc, char **argv)
{
    /* 引数のコピー */

    res = Py_Main(argc, argv_copy);

    /* メモリとかの後処理 */

    return res;
}

Modules/main.c

/* Main program */

int
Py_Main(int argc, wchar_t **argv)
{
    while ((c = _PyOS_GetOpt(argc, argv, PROGRAM_OPTS)) != EOF) {
        /* option の処理 */
    }

    Py_Initialize();

Modules/main.c

/* Py_Main 続き */
    if (command) { // -c で起動
        sts = run_command(command, &cf);
        free(command);
    } else if (module) { // -m で起動
        sts = RunModule(module, 1);
    }
    else {
        /* REPL の起動. */
        if (filename == NULL && stdin_is_interactive) {
            Py_InspectFlag = 0; /* do exit on SystemExit */
            /* Startup File を読み込む */
            RunStartupFile(&cf);
        }
        /* XXX */

Modules/main.c

/* Py_Main 続き */

        // -1 は「未実行」の意味
        sts = -1;               /* keep track of whether we've already run __main__ */

        if (filename != NULL) {
            // run_file との違いは??
            sts = RunMainFromImporter(filename);
        }

        if (sts==-1 && filename!=NULL) {
            fp = _Py_wfopen(filename, L"r");

            /* エラー処理などなど */
        }

Modules/main.c

/* Py_Main 続き */

        // .py (.pyc, .pyo) ファイルの実行
        if (sts == -1)
            sts = run_file(fp, filename, &cf);
    }

    Py_Finalize();
}

Modules/main.c

static int
RunMainFromImporter(wchar_t *filename)
{
    /* ... */
    sts = RunModule(L"__main__", 0);
    return sts != 0;

error:
    /* ... */
}

Modules/main.c

static int RunModule(wchar_t *modname, int set_argv0)
{
    /* ... */
    result = PyObject_Call(runmodule, runargs, NULL);

    if (result == NULL) {
        return -1;
    }

    return 0;
}

Modules/main.c

static int
run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf)
{
    /* ... */

    run = PyRun_AnyFileExFlags(fp, filename_str, filename != NULL, p_cf);

    return run != 0;
}

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);

}

Python/pythonrun.c

int
PyRun_InteractiveLoopFlags(FILE *fp, const char *filename, PyCompilerFlags *flags)
{
    /* ... */

    for (;;) { // REPL
        ret = PyRun_InteractiveOneFlags(fp, filename, flags);
        PRINT_TOTAL_REFS();
        if (ret == E_EOF)
            return 0;
    }
}

Python/pythonrun.c

int
PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
                        PyCompilerFlags *flags)
{
    m = PyImport_AddModule("__main__");
    /* __main__.__file__ の設定 */

    len = strlen(filename);
    ext = filename + len - (len > 4 ? 4 : 0);

Python/pythonrun.c

/* PyRun_SimpleFileExFlags 続き */

    if (maybe_pyc_file(fp, filename, ext, closeit)) {
        /* Try to run a pyc file. First, re-open in binary */

        /* Turn on optimization if a .pyo file is given */

        v = run_pyc_file(fp, filename, d, d, flags);
    } else {
        v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
                              closeit, flags);
    }

    /* ... */
}

Python/pythonrun.c

PyObject *
PyRun_FileExFlags(FILE *fp, const char *filename, int start, PyObject *globals,
                  PyObject *locals, int closeit, PyCompilerFlags *flags)
{
    PyObject *ret;
    mod_ty mod;
    PyArena *arena = PyArena_New(); /* メモリ領域確保 */
    if (arena == NULL)
        return NULL;

    // parsing
    mod = PyParser_ASTFromFile(fp, filename, NULL, start, 0, 0,
                               flags, NULL, arena);

Arena, Pool, Block

Arena, Pool は Python のメモリプールの仕組み.

  • Arena (256 KB) = Pool * 64
  • Pool (4 KB) = Block * n
  • Block (8-256 Bytes)

Python/pythonrun.c

/* PyRun_FileExFlags 続き */

    /* ... */

    // executing ここは @mayahjp 担当
    ret = run_mod(mod, filename, globals, locals, flags, arena);
    PyArena_Free(arena);
    return ret;
}

Python/pythonrun.c

mod_ty
PyParser_ASTFromFile(FILE *fp, const char *filename, const char* enc,
                     int start, char *ps1,
                     char *ps2, PyCompilerFlags *flags, int *errcode,
                     PyArena *arena)
{
    /* ... */
    node *n = PyParser_ParseFileFlagsEx(fp, filename, enc,
                                      &_PyParser_Grammar,
                            start, ps1, ps2, &err, &iflags);
    /* ... */

Python/pythonrun.c

/* PyParser_ASTFromFile 続き */

    if (n) {
        flags->cf_flags |= iflags & PyCF_MASK;
        mod = PyAST_FromNode(n, flags, filename, arena);
        PyNode_Free(n);
        return mod;
    }
    /* ... */
}

Parser/parsetok.c

node *
PyParser_ParseFileFlagsEx(FILE *fp, const char *filename,
                          const char *enc, grammar *g, int start,
                          char *ps1, char *ps2, perrdetail *err_ret, int *flags)
{
    /* ... */
    if ((tok = PyTokenizer_FromFile(fp, (char *)enc, ps1, ps2)) == NULL) {
        err_ret->error = E_NOMEM;
        return NULL;
    }
    tok->filename = filename;
    return parsetok(tok, g, start, err_ret, flags);
}

Python/ast.c

mod_ty
PyAST_FromNode(const node *n, PyCompilerFlags *flags, const char *filename,
               PyArena *arena)
{
    /* ast_for_stmt を呼ぶ */
}

Python/ast.c

static stmt_ty
ast_for_stmt(struct compiling *c, const node *n)
{
    /* 文の種類で switch-case */
    /* それぞれの文に対応した ast_for_* を呼ぶ */
}

関数アノテーション

PEP 3107 で入った関数アノテーションがどう parsing されるか見てみよう.

  • ast_for_funcdef
    • ast_for_arguments <- ここ!
    • ast_for_expr
    • ast_for_suite