大師兄的Python源碼學(xué)習(xí)筆記(三十五): 模塊的動態(tài)加載機(jī)制(二)
大師兄的Python源碼學(xué)習(xí)筆記(三十七): 模塊的動態(tài)加載機(jī)制(四)
三、import機(jī)制的實(shí)現(xiàn)
- 通過黑盒探索,可以發(fā)現(xiàn)Python的import機(jī)制可分為三個不同的功能:
- Python運(yùn)行時的全局module pool的維護(hù)和搜索蕉拢;
- 解析與搜索module路徑的樹狀結(jié)構(gòu);
- 對不同文件格式的module的動態(tài)加載機(jī)制好乐。
- 雖然Python中import的表現(xiàn)千變?nèi)f化营搅,但終歸可以總結(jié)為
import x.y.z
的默認(rèn)形式掺炭。 - 從前面的章節(jié)可以看到,import機(jī)制的起點(diǎn)是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ù)是核心箕速,它的功能是將args和kwds中所包含的所有對象按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ù)實(shí)際上是一個PyTupleObject對象盐茎,包含了builtin__import__函數(shù)運(yùn)行所需要的所有參數(shù)和信息,它是虛擬機(jī)在執(zhí)行IMPORT_NAME指令時打包產(chǎn)生的徙赢,這里虛擬機(jī)進(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);
}
... ...
- 在上面的代碼中咆蒿,虛擬機(jī)會對import動作上鎖和解鎖东抹,目的是為了同步不同的線程對同一個module的import動作。
- 代碼中的fromlist通常是Py_None,但當(dāng)Python虛擬機(jī)進(jìn)行類似
from x impory y,z
這樣的動作時沃测,fromlist就成為一個諸如(y,z)這樣的PyTupleObject對象缭黔。