search
LoginSignup
19
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Pythonインタプリタをショボーンインタプリタに改造する

この記事は Pythonインタプリタをショボーンインタプリタに改造する - narupoのブログ からの転載になります。

Pythonインタプリタをショボーンインタプリタにする

こんにちは、narupoです。

Pythonインタプリタも長いあいだ使ってると飽きるものです。
そこでインタプリタを改造して気分を変えることにしました。

インタプリタに(´・ω・`)ショボーンを表示させればインタプリタが可愛くなるかも?!
果たして本当にそうなるのでしょうか。

やってみたいと思います。

CPythonをゲットする

Pythonには色々な実装がありますが、今回改造するのは最も一般的と思われるCPythonの実装です。

Gitを使ってCPythonのプロジェクトをクローンします。

$ git clone https://github.com/python/cpython.git

あとはconfigureとやってmakeしてPythonをビルドします。
環境はWindows10のWSLを使っています。Ubuntu16.04ですね。なので生成されるPythonがpython.exeになってますね。

$ cd cpython
$ ./configure
$ make
$ python.exe

CPythonを改造する

Programs/python.cにPythonプログラムのエントリーポイントがありました。

#ifdef MS_WINDOWS
int
wmain(int argc, wchar_t **argv)
{
    return Py_Main(argc, argv);
}
#else
int
main(int argc, char **argv)
{
    return Py_BytesMain(argc, argv);
}
#endif

Windows10のWSL(Ubuntu16.04)の環境では後者のmainが呼ばれます。

Py_BytesMainModules/main.cで定義されています。

int
Py_BytesMain(int argc, char **argv)
{
    _PyArgv args = {
        .argc = argc,
        .use_bytes_argv = 1,
        .bytes_argv = argv,
        .wchar_argv = NULL};
    return pymain_main(&args);
}

pymain_mainも同ファイル内に定義されています。

static int
pymain_main(_PyArgv *args)
{
    PyStatus status = pymain_init(args);
    if (_PyStatus_IS_EXIT(status)) {
        pymain_free();
        return status.exitcode;
    }
    if (_PyStatus_EXCEPTION(status)) {
        pymain_exit_error(status);
    }

    return Py_RunMain();
}

Py_RunMainも同様です。

int
Py_RunMain(void)
{
    int exitcode = 0;

    pymain_run_python(&exitcode);

    if (Py_FinalizeEx() < 0) {
        /* Value unlikely to be confused with a non-error exit status or
           other special meaning */
        exitcode = 120;
    }

    pymain_free();

    if (_Py_UnhandledKeyboardInterrupt) {
        exitcode = exit_sigint();
    }

    return exitcode;
}

pymain_run_pythonの中身はなんじゃらほい?

static void
pymain_run_python(int *exitcode)
{
    ...
    pymain_header(config);
    ...
    else {
        *exitcode = pymain_run_stdin(config, &cf);
    }
}

pymain_headerが対話モードのバージョンとかを表示している関数です。
対話モードではpymain_run_stdinが呼ばれるようです。

先にpymain_headerを見てみます。

static void
pymain_header(const PyConfig *config)
{
    if (config->quiet) {
        return;
    }

    if (!config->verbose && (config_run_code(config) || !stdin_is_interactive(config))) {
        return;
    }

    fprintf(stderr, "Python %s on %s\n", Py_GetVersion(), Py_GetPlatform());
    if (config->site_import) {
        fprintf(stderr, "%s\n", COPYRIGHT);
    }
}

バージョンとかコピーライトを表示していますね。stderrに出力しているようです。
ここのPythonをショボーンに変えておきましょう。

    fprintf(stderr, "(´・ω・`)ショボーン %s on %s\n", Py_GetVersion(), Py_GetPlatform());

pymain_run_stdinに戻ります。

static int
pymain_run_stdin(PyConfig *config, PyCompilerFlags *cf)
{
    ...
    int run = PyRun_AnyFileExFlags(stdin, "<stdin>", 0, cf);
    ...
}

PyRun_AnyFileExFlagsを見てみましょう。
Python/pythonrun.cで定義されています。

int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
                     PyCompilerFlags *flags)
{
    ...
        int err = PyRun_InteractiveLoopFlags(fp, filename, flags);
    ...
}

PyRun_InteractiveLoopFlagsが呼ばれています。この関数を見てみましょう。

int
PyRun_InteractiveLoopFlags(FILE *fp, const char *filename_str, PyCompilerFlags *flags)
{
    ...
    do {
        ret = PyRun_InteractiveOneObjectEx(fp, filename, flags);
    } while (ret != E_EOF);
    ...
}

do while文の中でstdinを監視してループしているようです。
ここがPythonの対話モードでループされている所ですね。

ループの所にショボーンを追加しときましょう。

    ...
    do {
        fprintf(stderr, ">>> (´・ω・`)ショボーン\n");
        ret = PyRun_InteractiveOneObjectEx(fp, filename, flags);
        ...
    } while (ret != E_EOF);
    ...

これでショボーンインタプリタができました。
エラー時のカワイソスも追加します。

    ...
    do {
        fprintf(stderr, ">>> (´・ω・`)ショボーン\n");
        ret = PyRun_InteractiveOneObjectEx(fp, filename, flags);
        if (ret == -1 && PyErr_Occurred()) {
            /* Prevent an endless loop after multiple consecutive MemoryErrors
             * while still allowing an interactive command to fail with a
             * MemoryError. */
            if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
                if (++nomem_count > 16) {
                    PyErr_Clear();
                    err = -1;
                    break;
                }
            } else {
                nomem_count = 0;
            }
            PyErr_Print();

            // エラー出力のあとにカワイソスを表示
            fprintf(stderr, ">>> (´・ω・`)カワイソス\n");

            flush_io();
        } else {
            nomem_count = 0;
        }
#ifdef Py_REF_DEBUG
        if (show_ref_count) {
            _PyDebug_PrintTotalRefs();
        }
#endif
    } while (ret != E_EOF);
    ...

動作風景

↓がショボーンインタプリタの動作風景です。

0.gif

おわりに

しばらくこのインタプリタを使ってみた感想ですが、ショボーンがうざかったです。
みなさんもインタプリタを改造して気分を変えてみましょう。

以上、narupoでした。

関連記事

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
19
Help us understand the problem. What are the problem?