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
列表。
当代码使用 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
列表。