pip如何告诉Python如何导入C扩展
How does pip tell Python how to import C extensions
我希望以可移植的方式使用 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。
我想弄明白的是-
How/where pip 是否与 Python 相关,即 sysv_ipc
将从该特定文件导入?
Pip 在 Python 如何处理扩展模块导入方面没有任何作用。 Python 所需要的只是扩展模块文件本身,前提是它的格式受当前 OS 支持,并且该文件位于 sys.path
搜索路径的目录中。
Pip 只负责确保构成项目分发的文件最终位于 sys.path
位置。您找到的 .dist-info
目录是包元数据的一部分,由 pip 和 importlib.metadata
用于卸载、依赖项跟踪和报告等操作。导入时不使用这些文件。
您还没有确切地分享您是如何尝试导入扩展模块的,或者这是如何失败的,所以我无法评论您出了什么问题。
但是当一切正常时,从动态加载的共享对象库中导入模块与导入常规模块非常相似:
- Python 在
sys.path
列表中的所有目录中搜索与导入名称匹配的文件和目录,使用 PathFinder object. It knows to look for extension modules based on the file extension (the file extensions supported depend on your OS, see importlib.machinery.EXTENSION_SUFFIXES
作为列表)。
- 如果找到具有与导入名称匹配的扩展后缀的文件,则
importlib.machinery.ExtensionFileLoader
class 用于加载库。
加载的意思是:使用一个OS依赖的动态加载函数加载文件中的代码,然后访问一个入口点函数(通常是PyInit_<modulename>
)来获取模块命名空间。见 documentation on creating extension modules. For .so
files the Python/dynload_shlib.c
file implements the loader but there are other dynload_
implementations in the same directory. To load an .so
file Python passes the file path (containing at least one /
slash) to the 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.so
、sysv_ipc.abi3.so
和 sysv_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'
我希望以可移植的方式使用 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。
我想弄明白的是-
How/where pip 是否与 Python 相关,即 sysv_ipc
将从该特定文件导入?
Pip 在 Python 如何处理扩展模块导入方面没有任何作用。 Python 所需要的只是扩展模块文件本身,前提是它的格式受当前 OS 支持,并且该文件位于 sys.path
搜索路径的目录中。
Pip 只负责确保构成项目分发的文件最终位于 sys.path
位置。您找到的 .dist-info
目录是包元数据的一部分,由 pip 和 importlib.metadata
用于卸载、依赖项跟踪和报告等操作。导入时不使用这些文件。
您还没有确切地分享您是如何尝试导入扩展模块的,或者这是如何失败的,所以我无法评论您出了什么问题。
但是当一切正常时,从动态加载的共享对象库中导入模块与导入常规模块非常相似:
- Python 在
sys.path
列表中的所有目录中搜索与导入名称匹配的文件和目录,使用 PathFinder object. It knows to look for extension modules based on the file extension (the file extensions supported depend on your OS, seeimportlib.machinery.EXTENSION_SUFFIXES
作为列表)。 - 如果找到具有与导入名称匹配的扩展后缀的文件,则
importlib.machinery.ExtensionFileLoader
class 用于加载库。
加载的意思是:使用一个OS依赖的动态加载函数加载文件中的代码,然后访问一个入口点函数(通常是PyInit_<modulename>
)来获取模块命名空间。见 documentation on creating extension modules. For .so
files the Python/dynload_shlib.c
file implements the loader but there are other dynload_
implementations in the same directory. To load an .so
file Python passes the file path (containing at least one /
slash) to the 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
:
$ 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.so
、sysv_ipc.abi3.so
和 sysv_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'