【问题标题】:How does pip tell Python how to import C extensionspip 如何告诉 Python 如何导入 C 扩展
【发布时间】:2021-07-24 13:11:32
【问题描述】:

我希望以可移植的方式使用sysv_ipc 库。

我安装了它:

pip3 install sysv_ipc

然后来自 Python:

import sysv_ipc
sysv_ipc.__file__

# Output:
# /home/x/.local/lib/python3.9/site-packages/sysv_ipc.cpython-39-x86_64-linux-gnu.so

如果我将该文件复制到文件夹 pip uninstall 库中,然后从该文件夹打开 python 并尝试相同的导入,它会失败。

我尝试检查还安装了什么,发现:

/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info
/home/x/.local/lib/python3.9/site-packages/sysv_ipc.cpython-39-x86_64-linux-gnu.so
/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info/INSTALLER
/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info/LICENSE
/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info/METADATA
/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info/RECORD
/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info/REQUESTED
/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info/WHEEL
/home/x/.local/lib/python3.9/site-packages/sysv_ipc-1.1.0.dist-info/top_level.txt

我也没有在setup.py里面找到线索。

我想弄清楚的是-

pip 如何/在哪里与要从该特定文件导入 sysv_ipc 的 Python 相关?

【问题讨论】:

  • 我认为您真的在询问 Python 路径。无论是 c 扩展名还是 .py 文件都不会对答案产生太大影响。 (虽然我个人对细节没有足够的信心来给出一个好的答案)
  • 如何导入失败,你把文件移到哪里了?
  • @MartijnPieters 我在文件的位置打开了一个 python 提示符。如果我将文件重命名为“sysv_ipc.so”,则导入有效,但不能使用原始名称(sysv_ipc.xx.yy..)。我认为 pip 以某种方式注入了“sysv_ipc”所指文件的知识
  • @Jay 那么您的 Python 版本与您安装 sysv_ipc 的版本不匹配。使用pip -Vpython -V 检查您的版本。您删除了 ABI 标识符,但该模块是否实际运行取决于 Python C 在被要求执行工作时调用的内容。查看我更新的答案(我删除了我最初的错误猜测,我真的不应该在深夜完全在手机屏幕上写答案)。
  • @Jay 这也说明了为什么您需要共享您的代码和命令。如果您预先分享您运行的确切命令、输出是什么以及重命名文件会改变结果,我们可以帮助您并更好地理解您得出的(不正确的)结论。跨度>

标签: python-3.x pip python-import python-c-api python-internals


【解决方案1】:

Pip 在 Python 如何处理扩展模块导入方面没有任何作用。 Python 需要的只是扩展模块文件本身,前提是它采用当前操作系统支持的格式,并且该文件位于 sys.path 搜索路径上的目录中。

Pip 仅负责确保构成项目分发的文件最终位于 sys.path 位置。您找到的 .dist-info 目录是包元数据的一部分,由 pip 和 importlib.metadata 用于卸载、依赖项跟踪和报告等操作。导入时不使用这些文件。

您尚未确切分享您尝试导入扩展模块的方式或失败的原因,因此我无法评论您出了什么问题。

但是当一切正常时,从动态加载的共享对象库中导入模块的工作方式与导入常规模块很相似:

  1. Python 使用PathFinder objectsys.path 列表中的所有目录中搜索与导入名称匹配的文件和目录。它知道根据文件扩展名查找扩展模块(支持的文件扩展名取决于您的操作系统,请参阅importlib.machinery.EXTENSION_SUFFIXES 获取列表)。
  2. 如果找到与导入名称匹配的带有扩展后缀的文件,则使用 importlib.machinery.ExtensionFileLoader class 加载库。

加载是指:使用依赖于操作系统的动态加载函数加载文件中的代码,然后访问入口点函数(通常为PyInit_<modulename>)以获取模块命名空间。请参阅documentation on creating extension modules。对于.so 文件,Python/dynload_shlib.c file 实现了加载器,但在同一目录中还有其他dynload_ 实现。要加载.so 文件,Python 将文件路径(至少包含一个/ 斜杠)传递给dlopen() function

至于您的情况可能出了什么问题:您使用的 Python 解释器与用于安装项目的解释器不同。请注意,扩展模块文件名在模块名称后包含一个字符串,用于标识 Python ABI (Application Binary Interface):

sysv_ipc.cpython-39-x86_64-linux-gnu.so
######## ^^^^^^^^^^^^^^^^^^^^^^^^^^^
module   ABI identifier

该标识符可以将多个 Python 版本的扩展文件安装到同一目录中。请查看importlib.machinery.EXTENSION_SUFFIXES,检查您的特定 Python 二进制文件接受哪些扩展:

$ python3 -c "from importlib.machinery import EXTENSION_SUFFIXES;print(EXTENSION_SUFFIXES)"
['.cpython-39-x86_64-linux-gnu.so', '.abi3.so', '.so']

输出告诉我这个解释器只会寻找sysv_ipc.cpython-39-x86_64-linux-gnu.sosysv_ipc.abi3.sosysv_ipc.so文件名来加载。

给定的 Python 版本支持扩展模块可能想要使用的特定导出 C 函数,并且 ABI 会告诉您它是针对哪个版本编译的。使用短 abi3.so 后缀的扩展是针对 stable ABI 编译的,这是保证在许多 Python 版本中存在的 Python 功能的较小子集。

虽然您可以重命名扩展文件以仅使用最短后缀 ([module_name].so),但它在很大程度上取决于动态加载的机器代码调用的 Python 功能是否仍可使用不同的 Python 版本。

这是一个快速演示,显示您可以从任意目录导入 sysv_ipc 动态库,前提是我使用正确的 Python 版本:

$ virtualenv /demo
... creating a virtualenv ...
done.
$ cd /demo
demo/ $ source bin/activate
(demo) /demo/ $ pip install sysv_ipc
Collecting sysv_ipc
... installing ...
Successfully installed sysv-ipc-1.1.0
(demo) /demo/ $ mkdir newdir
(demo) /demo/ $ cp lib/python3.9/site-packages/sysv_ipc.cpython-39-x86_64-linux-gnu.so newdir
(demo) /demo/ $ pip uninstall -y sysv_ipc
Found existing installation: sysv-ipc 1.1.0
... uninstalling ...
  Successfully uninstalled sysv-ipc-1.1.0
(demo) /demo/ $ cd newdir/
(demo) /demo/newdir/ $ python
Python 3.9.2 (default, Mar 15 2021, 17:53:50)
[Clang 7.0.1 (tags/RELEASE_701/final)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sysv_ipc
>>> sysv_ipc.__file__
'/demo/newdir/sysv_ipc.cpython-39-x86_64-linux-gnu.so'

【讨论】:

    猜你喜欢
    • 2021-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多