大師兄的Python源碼學(xué)習(xí)筆記(三十六): 模塊的動態(tài)加載機制(三)

大師兄的Python源碼學(xué)習(xí)筆記(三十五): 模塊的動態(tài)加載機制(二)
大師兄的Python源碼學(xué)習(xí)筆記(三十七): 模塊的動態(tài)加載機制(四)

三、import機制的實現(xiàn)

  • 通過黑盒探索,可以發(fā)現(xiàn)Python的import機制可分為三個不同的功能:
  1. Python運行時的全局module pool的維護和搜索;
  2. 解析與搜索module路徑的樹狀結(jié)構(gòu);
  3. 對不同文件格式的module的動態(tài)加載機制。
  • 雖然Python中import的表現(xiàn)千變?nèi)f化,但終歸可以總結(jié)為 import x.y.z的默認(rèn)形式。
  • 從前面的章節(jié)可以看到,import機制的起點是builtin module中的__import__操作,也就是builtin__import___函數(shù):
Python\bltinmodule.c

static PyObject *
builtin___import__(PyObject *self, PyObject *args, PyObject *kwds)
{
    static char *kwlist[] = {"name", "globals", "locals", "fromlist",
                             "level", 0};
    PyObject *name, *globals = NULL, *locals = NULL, *fromlist = NULL;
    int level = 0;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "U|OOOi:__import__",
                    kwlist, &name, &globals, &locals, &fromlist, &level))
        return NULL;
    return PyImport_ImportModuleLevelObject(name, globals, locals,
                                            fromlist, level);
}
  • 可以看出在這里的PyArg_ParseTupleAndKeywords函數(shù)是核心,它的功能是將argskwds中所包含的所有對象按format中指定的格式解析成各種目標(biāo)對象:
Python\getargs.c

/* Support for keyword arguments donated by
   Geoff Philbrick <philbric@delphi.hks.com> */

/* Return false (0) for error, else true. */
int
PyArg_ParseTupleAndKeywords(PyObject *args,
                            PyObject *keywords,
                            const char *format,
                            char **kwlist, ...)
{
    int retval;
    va_list va;

    if ((args == NULL || !PyTuple_Check(args)) ||
        (keywords != NULL && !PyDict_Check(keywords)) ||
        format == NULL ||
        kwlist == NULL)
    {
        PyErr_BadInternalCall();
        return 0;
    }

    va_start(va, kwlist);
    retval = vgetargskeywords(args, keywords, format, kwlist, &va, 0);
    va_end(va);
    return retval;
}
  • 所謂目標(biāo)對象可以是Python中的對象,也可以是C的原生類型。
  • 這里的args參數(shù)實際上是一個PyTupleObject對象,包含了builtin__import__函數(shù)運行所需要的所有參數(shù)和信息,它是虛擬機在執(zhí)行IMPORT_NAME指令時打包產(chǎn)生的,這里虛擬機進(jìn)行了一個逆動作,即將PyTupleObject對象拆開,重新獲得當(dāng)初的參數(shù)。
  • 指令解析格式的format參數(shù)中可用的格式字符非常多,這里簡單介紹builtin___import__用到的字符:
{"name", "globals", "locals", "fromlist","level", 0}
U|OOOi:__import__

U:代表目標(biāo)對象是一個char*,用來將tuple中的PyUnicodeObject對象解析成char*。
i:用來將tuple中的PyIntObject對象解析為int類型的值。
O:代表解析的目標(biāo)對象依然是一個Python中的核發(fā)對象,通常表示不進(jìn)行任何解析和轉(zhuǎn)換。
|:非格式字符,表示其后所帶的格式字符是可選的。
::指示格式字符到此結(jié)束,其后所帶的字符串用于在解析過程中出錯時輸出錯誤信息時使用。

  • 在完成拆包動作后,Python進(jìn)入了PyImport_ImportModuleLevelObject:
Python\import.c

PyObject *
PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
                                 PyObject *locals, PyObject *fromlist,
                                 int level)
{
    _Py_IDENTIFIER(_handle_fromlist);
    PyObject *abs_name = NULL;
    PyObject *final_mod = NULL;
    PyObject *mod = NULL;
    PyObject *package = NULL;
    PyInterpreterState *interp = PyThreadState_GET()->interp;
    int has_from;
    ... ...
    mod = PyImport_GetModule(abs_name);
    if (mod != NULL && mod != Py_None) {
        _Py_IDENTIFIER(__spec__);
        _Py_IDENTIFIER(_initializing);
        _Py_IDENTIFIER(_lock_unlock_module);
        PyObject *value = NULL;
        PyObject *spec;
        int initializing = 0;

        /* Optimization: only call _bootstrap._lock_unlock_module() if
           __spec__._initializing is true.
           NOTE: because of this, initializing must be set *before*
           stuffing the new module in sys.modules.
         */
        spec = _PyObject_GetAttrId(mod, &PyId___spec__);
        if (spec != NULL) {
            value = _PyObject_GetAttrId(spec, &PyId__initializing);
            Py_DECREF(spec);
        }
        if (value == NULL)
            PyErr_Clear();
        else {
            initializing = PyObject_IsTrue(value);
            Py_DECREF(value);
            if (initializing == -1)
                PyErr_Clear();
            if (initializing > 0) {
                value = _PyObject_CallMethodIdObjArgs(interp->importlib,
                                                &PyId__lock_unlock_module, abs_name,
                                                NULL);
                if (value == NULL)
                    goto error;
                Py_DECREF(value);
            }
        }
    }
    else {
        Py_XDECREF(mod);
        mod = import_find_and_load(abs_name);
        if (mod == NULL) {
            goto error;
        }
    }

    has_from = 0;
    if (fromlist != NULL && fromlist != Py_None) {
        has_from = PyObject_IsTrue(fromlist);
        if (has_from < 0)
            goto error;
    }
    if (!has_from) {
        Py_ssize_t len = PyUnicode_GET_LENGTH(name);
        if (level == 0 || len > 0) {
            Py_ssize_t dot;

            dot = PyUnicode_FindChar(name, '.', 0, len, 1);
            if (dot == -2) {
                goto error;
            }

            if (dot == -1) {
                /* No dot in module name, simple exit */
                final_mod = mod;
                Py_INCREF(mod);
                goto error;
            }

            if (level == 0) {
                PyObject *front = PyUnicode_Substring(name, 0, dot);
                if (front == NULL) {
                    goto error;
                }

                final_mod = PyImport_ImportModuleLevelObject(front, NULL, NULL, NULL, 0);
                Py_DECREF(front);
            }
            else {
                Py_ssize_t cut_off = len - dot;
                Py_ssize_t abs_name_len = PyUnicode_GET_LENGTH(abs_name);
                PyObject *to_return = PyUnicode_Substring(abs_name, 0,
                                                        abs_name_len - cut_off);
                if (to_return == NULL) {
                    goto error;
                }

                final_mod = PyImport_GetModule(to_return);
                Py_DECREF(to_return);
                if (final_mod == NULL) {
                    PyErr_Format(PyExc_KeyError,
                                 "%R not in sys.modules as expected",
                                 to_return);
                    goto error;
                }
            }
        }
        else {
            final_mod = mod;
            Py_INCREF(mod);
        }
    }
    else {
        final_mod = _PyObject_CallMethodIdObjArgs(interp->importlib,
                                                  &PyId__handle_fromlist, mod,
                                                  fromlist, interp->import_func,
                                                  NULL);
    }
... ...
  • 在上面的代碼中,虛擬機會對import動作上鎖和解鎖,目的是為了同步不同的線程對同一個moduleimport動作。
  • 代碼中的fromlist通常是Py_None,但當(dāng)Python虛擬機進(jìn)行類似from x impory y,z這樣的動作時,fromlist就成為一個諸如(y,z)這樣的PyTupleObject對象。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容