Where/how 名称“posix”是否由导入语句解析?

Where/how does the name `posix` get resolved by an import statement?

当代码使用 import posix 时,幕后(在 CPython 3.6.0 中)会发生什么?该模块没有 __file__ 属性。在详细模式下启动解释器时,我看到这一行:

import 'posix' # <class '_frozen_importlib.BuiltinImporter'>

它已经存在于 sys.modules 新打开的解释器中,导入它只是将一个名称绑定到现有模块。

我正在尝试查看 os.lstat 在我的平台上的实现细节,以确定它是否以及何时使用 os.stat

在这里,提供比您可能需要的更多的详细信息。


posix是内置模块。当您听到 "built-in module" 时,您可能会想到普通的标准库模块,或者您可能会想到用 C 编写的模块,但是 posix 比大多数内置的都多。

posix模块是用C写的,在Modules/posixmodule.c。然而,虽然大多数 C 模块,甚至是标准库 C 模块,都被编译为 .so.pyd 文件并像常规 Python 模块一样放置在导入路径上,但 posix 实际上得到直接编译到 Python 可执行文件本身。


CPython 导入系统的内部细节之一是 PyImport_Inittab array:

extern struct _inittab _PyImport_Inittab[];

struct _inittab *PyImport_Inittab = _PyImport_Inittab;

这是一个 struct _inittab 的数组,由名称和具有该名称的模块的 C 模块初始化函数组成。此处列出的模块是内置的。

此数组最初设置为 _PyImport_Inittab,它来自 Modules/config.c(或 PC/config.c,具体取决于您的 OS,但此处并非如此)。不幸的是,Modules/config.c 是在 Python 构建过程中从 Modules/config.c.in 生成的,所以我无法向您展示源代码 link,但这里是它看起来像的一部分我生成文件:

struct _inittab _PyImport_Inittab[] = {

        {"_thread", PyInit__thread},
        {"posix", PyInit_posix},
        // ...

如您所见,有一个 posix 模块的条目,以及模块初始化函数 PyInit_posix


作为导入系统的一部分,当尝试加载模块时,Python 会通过 sys.meta_path,模块列表 finders. One of these finders is responsible for performing the sys.path search you're likely more familiar with, but one of the others is _frozen_importlib.BuiltinImporter, responsible for finding built-in modules like posix. When Python tries that finder, it runs the finder's find_spec 方法:

@classmethod
def find_spec(cls, fullname, path=None, target=None):
    if path is not None:
        return None
    if _imp.is_builtin(fullname):
        return spec_from_loader(fullname, cls, origin='built-in')
    else:
        return None

使用 _imp.is_builtin 搜索 PyImport_Inittab 以获取 "posix" 名称。搜索找到名称,因此 find_spec returns 一个模块规范表示内置模块的加载程序应该处理创建此模块的事实。 (加载器是 spec_from_loader 的第二个参数。这里是 cls,因为 BuiltinImporter 既是查找器又是加载器。)

Python然后运行加载器的create_module方法生成模块对象:

@classmethod
def create_module(self, spec):
    """Create a built-in module"""
    if spec.name not in sys.builtin_module_names:
        raise ImportError('{!r} is not a built-in module'.format(spec.name),
                          name=spec.name)
    return _call_with_frames_removed(_imp.create_builtin, spec)

委托给 _imp.create_builtin,后者搜索 PyImport_Inittab 模块名称并运行相应的初始化函数。

(_call_with_frames_removed(x, y) 只是调用 x(y),但 part of the import system 将其视为从堆栈跟踪中删除 importlib 帧的神奇指示器,这就是为什么你永远看不到那些导入出错时堆栈跟踪中的帧。)


如果您想查看更多涉及的代码路径,可以查看 Lib/importlib/_bootstrap.py, where most of the import implementation lives, Python/import.c, where most of the C part of the implementation lives, and Python/ceval.c,这是字节码解释器循环所在的位置,因此也是 import 语句开始执行的位置,才到达进口机械更核心的部分。

相关文档包括 section of the language reference on the import system, as well as PEPs 451 and 302. There isn't much documentation on built-in modules, although I did find a bit of documentation targeted toward people embedding Python in other programs, since they might want to modify PyImport_Inittab, and there is the sys.builtin_module_names 列表。