借助 .pth 文件完成多个 Python 解释器的合并

ops/2025/1/22 13:34:34/

相关搜索

  • conda 虚拟环境如何使用 ROS 的 Python 模块
  • conda 虚拟环境找不到 catkin_pkg

问题描述

如果你在 Ubuntu 20.04 中装了 conda,那么你的 Ubuntu 会有这些 Python 解释器:

  • /usr/bin/python3:系统的解释器 (版本为 3.8.10,含有 ROS 模块)
  • /opt/miniconda3/bin/python:base 环境的解释器
  • /opt/miniconda3/envs/*/bin/python:虚拟环境的解释器

对于 ROS 玩家而言,只要不使用 conda 就有很多好处:

  1. 不会因为环境变量导致各种奇怪的报错
  2. 只要用系统的 Python 解释器就万事大吉

但同时也有一些坏处:

  1. 不能使用不支持 Python 3.8 的库
  2. 万一把系统的 Python 解释器稿费了,可能就要重装系统了

如果既要用 conda 的虚拟环境 (version ≥ 系统解释器),又要用系统解释器的 ROS 模块,就需要通过 .pth 文件完成多个 Python 解释器的合并

试错:PYTHONPATH

Python 的模块通常是一个包含 .py 文件、二进制文件的文件夹,只要我们的 Python 解释器能找到这个文件夹就能调用对应的模块

如果我们设置了 PYTHONPATH 的值,那么这其中的路径会位于 `sys.path` 的最前方——也就是 Python 解释器在执行 import 时,会优先搜索这个路径下的模块

PYTHONPATH=/opt/ros/noetic/lib/python3/dist-packages

虚拟环境:

(tongzj) root@2c41537759b7:/home/workbench# python

Python 3.9.0 (default, Nov 15 2020, 14:28:56)

[GCC 7.3.0] :: Anaconda, Inc. on linux

Type "help", "copyright", "credits" or "license" for more information.

>>> import catkin_pkg

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

ModuleNotFoundError: No module named 'catkin_pkg'

>>> import sys

>>> sys.path

['', '/opt/ros/noetic/lib/python3/dist-packages', '/opt/miniconda3/envs/tongzj/lib/python39.zip', '/opt/miniconda3/envs/tongzj/lib/python3.9', '/opt/miniconda3/envs/tongzj/lib/python3.9/lib-dynload', '/opt/miniconda3/envs/tongzj/lib/python3.9/site-packages']

尽管如此,我们还是不能找到 catkin_pkg,那么又通过以下步骤对 PYTHONPATH 进行修正

系统解释器:

root@2c41537759b7:/home/workbench# python3
Python 3.8.10 (default, Nov  7 2024, 13:10:47) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import catkin_pkg
>>> catkin_pkg.__file__
'/usr/lib/python3/dist-packages/catkin_pkg/__init__.py'

export PYTHONPATH=/opt/ros/noetic/lib/python3/dist-packages:/usr/lib/python3/dist-packages

虚拟环境:

(tongzj) root@2c41537759b7:/home/workbench# python
Python 3.9.0 (default, Nov 15 2020, 14:28:56) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import catkin_pkg
>>> import sys
>>> sys.path
['', '/opt/ros/noetic/lib/python3/dist-packages', '/usr/lib/python3/dist-packages', '/opt/miniconda3/envs/tongzj/lib/python39.zip', '/opt/miniconda3/envs/tongzj/lib/python3.9', '/opt/miniconda3/envs/tongzj/lib/python3.9/lib-dynload', '/opt/miniconda3/envs/tongzj/lib/python3.9/site-packages'] 

现在可以正常导入模块了,但是如果使用 pip 的话,很容易就发现不对劲:虚拟环境应该使用 /opt/miniconda3/envs/tongzj/bin/pip,但是报错的来源都是 /usr/lib/python3 (也就是系统解释器)

或者说,虚拟环境使用了系统解释器的 pip —— 这是因为系统解释器的 pip 模块位于 /usr/lib/python3/dist-packages

虚拟环境:

(tongzj) root@2c41537759b7:/home/workbench# pip install torch==2.4.1 torchvision==0.19.1 torchaudio==2.4.1 --index-url https://download.pytorch.org/whl/cu121
Traceback (most recent call last):
File "/opt/miniconda3/envs/tongzj/bin/pip", line 7, in <module>
from pip._internal.cli.main import main
File "/usr/lib/python3/dist-packages/pip/_internal/cli/main.py", line 10, in <module>
from pip._internal.cli.autocompletion import autocomplete
File "/usr/lib/python3/dist-packages/pip/_internal/cli/autocompletion.py", line 9, in <module>
from pip._internal.cli.main_parser import create_main_parser
File "/usr/lib/python3/dist-packages/pip/_internal/cli/main_parser.py", line 7, in <module>
from pip._internal.cli import cmdoptions
File "/usr/lib/python3/dist-packages/pip/_internal/cli/cmdoptions.py", line 25, in <module>
from pip._internal.locations import USER_CACHE_DIR, get_src_prefix
File "/usr/lib/python3/dist-packages/pip/_internal/locations.py", line 19, in <module>
from pip._internal.utils import appdirs
File "/usr/lib/python3/dist-packages/pip/_internal/utils/appdirs.py", line 13, in <module>
from pip._vendor import appdirs as _appdirs
ImportError: cannot import name 'appdirs' from 'pip._vendor' (/usr/lib/python3/dist-packages/pip/_vendor/__init__.py)

如果我们可以把 /usr/lib/python3/dist-packages 放在 `sys.path` 的末尾就可以避免这个问题

解决方案

要让虚拟环境的解释器找到系统解释器的 ROS 模块,我们可以使用 .pth 文件来拓展 Python 模块的搜索路径

经过测试,Windows 和 Ubuntu 系统下的虚拟环境,site-packages 文件夹下的所有 .pth 文件都是有效的

虚拟环境:

(tongzj) root@2c41537759b7:/home/workbench# python

Python 3.9.0 (default, Nov 15 2020, 14:28:56)

[GCC 7.3.0] :: Anaconda, Inc. on linux

Type "help", "copyright", "credits" or "license" for more information.

>>> import sys

>>> sys.path

['', '/opt/ros/noetic/lib/python3/dist-packages', '/opt/miniconda3/envs/tongzj/lib/python39.zip', '/opt/miniconda3/envs/tongzj/lib/python3.9', '/opt/miniconda3/envs/tongzj/lib/python3.9/lib-dynload', '/opt/miniconda3/envs/tongzj/lib/python3.9/site-packages']

.pth 文件的格式如下 (可以有多行,每一行都是一个搜索路径):

/usr/lib/python3/dist-packages

而如何定位 site-packages 的位置?site-packages 目录下不仅有我们通过 pip install 安装的包,还有 pip 模块,所以有:

虚拟环境:

(tongzj) root@2c41537759b7:/home/workbench# python
Python 3.9.0 (default, Nov 15 2020, 14:28:56) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pip
>>> pip.__file__
'/opt/miniconda3/envs/gsicpslam/lib/python3.9/site-packages/pip/__init__.py'

故我们可以通过以下代码来进行 .pth 文件的读写

python">from pathlib import Pathimport pipSITE_PACKAGES = Path(pip.__file__).parent.parentclass PythonExtLibs:f = SITE_PACKAGES / "extlib.pth"@classmethoddef load(cls):return set(p for p in cls.f.read_text().splitlines() if p) if cls.f.is_file() else set()@classmethoddef dump(cls, ext):ext = [str(p.absolute()) for p in map(Path, ext) if p.is_dir()]cls.f.write_text("\n".join(ext)) if ext else cls.f.unlink(missing_ok=True)return ext@classmethoddef add(cls, paths):ext = cls.load()return cls.dump(ext | set(map(str, paths)))@classmethoddef remove(cls, paths):ext = cls.load()return cls.dump(ext - set(map(str, paths)))if __name__ == "__main__":print(PythonExtLibs.dump(["/usr/lib/python3/dist-packages"]))

.pth 文件中的路径被成功添加到 `sys.path` 的末尾,解决

虚拟环境:

(tongzj) root@2c41537759b7:/home/workbench# python
Python 3.9.0 (default, Nov 15 2020, 14:28:56) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/opt/ros/noetic/lib/python3/dist-packages', '/opt/miniconda3/envs/tongzj/lib/python39.zip', '/opt/miniconda3/envs/tongzj/lib/python3.9', '/opt/miniconda3/envs/tongzj/lib/python3.9/lib-dynload', '/opt/miniconda3/envs/tongzj/lib/python3.9/site-packages', '/usr/lib/python3/dist-packages']

潜在问题的解决

完成以上步骤后,虚拟环境不仅能找到自己的模块,还能找到系统解释器的模块

我们把系统解释器模块的路径添加到了 `sys.path` 的末尾,使得虚拟环境优先使用自己的模块,如果没有的话再使用系统解释器的模块

那么,不难猜测还可能有以下问题:

系统解释器虚拟环境问题
模块 1未发现
模块 2因为 pip 检测到已经安装该模块,无法在缺少该环境的一方进行 install
模块 3
模块 4因为两个环境同时具有一个模块,导致 pip uninstall 无法正确定位需要卸载的部分 (如下所示)

(tongzj) root@2c41537759b7:/home/workbench# pip install -r requirements.txt
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting opencv-contrib-python (from -r requirements.txt (line 1))
......
Attempting uninstall: click
Found existing installation: Click 7.0
Uninstalling Click-7.0:
Successfully uninstalled Click-7.0
Attempting uninstall: blinker
Found existing installation: blinker 1.4
error: uninstall-distutils-installed-package

× Cannot uninstall blinker 1.4
╰─> It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall. 

但这些都是由于 .pth 文件造成的,所以只需把 .pth 暂时屏蔽即可 (修改文件拓展名 / 删除) 


http://www.ppmy.cn/ops/152199.html

相关文章

MQ消息队列

1、消息队列特点 2、RabbitMQ

进程组成、状态、前趋图、资源图(高软6)

系列文章目录 2.2进程组成、状态、前趋图、资源图 文章目录 系列文章目录前言一、本节内容进程的组成进程真题前趋图资源图真题 总结 前言 本节讲明进程的组成与状态&#xff0c;以及前趋图、资源图。 一、本节内容 进程的组成 进程真题 AC 前趋图 资源图 真题 CB 总结 就…

MongoDB 备份与恢复综述

目录 一、基本概述 二、逻辑备份 1、全量备份 2、增量备份 3、恢复 三、物理备份 1、cp/tar/fsync 2、WiredTiger 热备份 3、恢复 四、快照备份 一、基本概述 MongoDB 是一种流行的 NoSQL 数据库&#xff0c;它使用文档存储数据&#xff0c;支持丰富的查询语言和索引…

Selenium工具使用Python 语言实现下拉框定位操作

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 我们通常遇到的下拉框有显性的下拉框和隐性的下拉框&#xff1b;有的下拉框还可以进行单选或多选操作&#xff0c;在selenium中如何实现下拉框的定位通常使用selec…

分布式 ID 生成策略:应用场景与 ShardingSphere 实现

在分布式系统中&#xff0c;分布式 ID&#xff08;Distributed ID&#xff09;是一种用于唯一标识各类数据记录的技术&#xff0c;通常用于数据库主键、日志、消息队列等场景。由于分布式系统的特点&#xff0c;各个服务实例通常会在不同的物理节点上进行独立运行&#xff0c;这…

面试题目1

文章目录 C语言函数调用详细过程C语言中const与C中const的区别关系运算符有哪些互斥锁与读写锁的区别gcc的编译过程 C语言函数调用详细过程 调用函数&#xff1a;当程序执行到函数调用语句时&#xff0c;会将当前函数的返回地址、参数列表等信息压入栈中&#xff0c;然后跳转到…

5、艰难的选择

“ 你好&#xff0c;我想让身体变得更健康一些。怎么办&#xff1f; ” “ 每天慢跑 8 公里。 ” “ 太麻烦了&#xff01;算了&#xff0c;以后再说吧。 ” …… “大夫你好&#xff0c;这病花多少钱都无所谓&#xff01;只要能治好&#xff0c;倾家荡产都 行。 ” 多数组…

软件测试—— 接口测试(HTTP和HTTPS)

软件测试—— 接口测试&#xff08;HTTP和HTTPS&#xff09; HTTP请求方法GET特点使用场景URL结构URL组成部分URL编码总结 POST特点使用场景请求结构示例 请求标头和响应标头请求标头&#xff08;Request Headers&#xff09;示例请求标头 响应标头&#xff08;Response Header…