名称`posix`在哪里/如何通过import语句解析?

时间:2017-03-09 23:18:20

标签: python linux python-import cpython

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

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

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

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

1 个答案:

答案 0 :(得分:3)

在这里,有更多细节,而不是你可能需要的。

posix是一个内置模块。当您听到“内置模块”时,您可能会想到普通的标准库模块,或者您可能会想到用C语言编写的模块,但posix比大多数模块更内置。

posix模块是用Modules/posixmodule.c中的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,具体取决于您的操作系统,但这不是这种情况)。不幸的是,Modules/config.c是在Python构建过程中从Modules/config.c.in生成的,因此我无法向您展示源代码链接,但这是我生成文件时的部分内容:

struct _inittab _PyImport_Inittab[] = {

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

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

作为导入系统的一部分,在尝试加载模块时,Python会通过sys.meta_path,即模块finders的列表。其中一个查找程序负责执行您可能更熟悉的sys.path搜索,但其中一个是_frozen_importlib.BuiltinImporter,负责查找posix等内置模块。当Python尝试该查找程序时,它运行查找程序的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_builtinPyImport_Inittab中搜索"posix"名称。搜索找到名称,因此find_spec返回一个模块规范,表示内置模块的加载器应该处理创建此模块的事实。 (加载器是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_builtinpart of the import system搜索PyImport_Inittab以获取模块名称并运行相应的初始化函数。

_call_with_frames_removed(x, y)只调用x(y),但Lib/importlib/_bootstrap.py将其视为从堆栈跟踪中剥离importlib帧的神奇指标,这就是为什么你永远不会看到这些帧的原因导入错误时的堆栈跟踪。)

如果您想查看更多涉及的代码路径,可以查看大多数导入实现所在的Python/import.cPython/ceval.c,其中大部分C部分都存在,和section of the language reference on the import system,它是字节码解释器循环所在的位置,因此是import语句的执行开始之前,它到达导入机制的更核心部分之前。

相关文档包括451,以及PEP 302a bit of documentation。关于内置模块的文档并不多,虽然我确实发现sys.builtin_module_names针对的是人们将Python嵌入到其他程序中,因为他们可能想要修改PyImport_Inittab,并且{{3}} } list。