(本文路径均从项目根目录下出发)
入口文件是Programs/python.c:
Windows会走wmain,主要是处理宽字符问题:
最后都会进pymain_main这个函数:
pymain_init(args)内部:
初始化工作,包括执行_PyRuntime_Initialize()、PyPreConfig_InitPythonConfig(&preconfig)、_Py_PreInitializeFromPyArgv(&preconfig, args)、PyConfig_InitPythonConfig(&config)、PyConfig_SetArgv(&config, args->argc, args->wchar_argv),函数里PyConfig类型的config变量很重要,它读取命令行参数、环境变量和配置文件,继续往下走,Py_InitializeFromConfig(&config),status作为一个PyState用来判断错误。这里有无异常都会进行config的clear内存释放,虽然这里config是栈变量,但其内部包含了指向堆的指针,需保证动态分配的内存也被正确释放,否则config被自动释放后,堆上的内存可能没引用但也没释放(内存泄漏
以下是关于PyState判断用到的宏函数(不止这三个):
Py_RunMain():
Py_RunMain主要就是内部调用了pymain_run_python(&exitcode):
pymain_run_python(&exitcode):
拿到了PyInterpreterState(通过当前线程拿到的)和config,断言是因为下面才开始设置Python中的sys.path,此前如果不为空说明有问题。
下面要开始设置sys.path了,也就python找包的搜索路径:
它先判断run_filename是否是一个包含__main__.py的目录,如果是,待会它会优先设置它到sys.path[0],但只是优先,在此之前还要做一个事情。
往下走可以看到,这里在设置sys.path之前先去导入readline和rlcompetor,防止后续因为命名冲突的问题不能导入这两个模块,当然这里也不一定会导入,pymain_import_readline内部会判断是否是交互式启动而选择性导入这两个模块。
然后就是设置sys.path[0]的部分,可以看到
刚刚如果你的fun_filename满足要求,这里会被优先设置为sys.path[0]。而且这里生成的目录名(如果是else if分支会被去掉文件名,只保留完整路径,相对路径也会被转换为绝对路径)是PyObject了(猜测应该是PyUnicode_Type),不会再存为C的宽字符wchar或字符char。
下面就是检查了一次path0,然后将其赋值给了interp->runtime->sys_path_0和interp->sysdict这个字典的"path"键的项,它就是Python层面的sys.path,然后插入到0号位置。
sys.path[0]设置好之后,再下来
关注一下_PyInterpreterState_SetRunningMain函数即可,它会将拿到当前的执行线程,并将当前解释器的threads_main指向这个线程,注意线程在此之前已经指向了这个解释器,所以在函数内部他还会判断下,这个是不是一一对应关系,如果不是会报错
下一篇文章,我们就将进入
看一看我们的脚本是如何被解释和运行的。