摘要 :不要复制带
venv的项目文件夹!否则你会在"pip 显示 1.1.0,Python 却跑 2.2.0"的迷宫里绕到怀疑人生。
一次"虚拟环境复制引发的血案":记一次 itsdangerous 版本混乱排查全过程
起因:一个看似简单的注册功能
我正在开发一个 Flask 应用,需要实现用户邮箱确认功能。按照惯例,使用 itsdangerous 生成带过期时间的 token:
python
from itsdangerous import URLSafeTimedSerializer
def generate_confirmation_token(self, expiration=3600):
s = URLSafeTimedSerializer(
current_app.config['SECRET_KEY'],
expires_in=expiration,
salt='confirm'
)
return s.dumps({'confirm': self.id})
结果一调用就报错:
css
TypeError: __init__() got an unexpected keyword argument 'expires_in'
???不是说 URLSafeTimedSerializer 支持 expires_in 吗?
第一重迷惑:版本到底是什么?
我查了下依赖:
powershell
pip list | select-string dang
# 输出:itsdangerous 1.1.0
但为了保险,我在代码里加了打印:
python
import itsdangerous
print("REAL version:", itsdangerous.__version__)
输出竟然是:
sql
REAL version: 2.2.0
pip 说 1.1.0,Python 说 2.2.0 ------ 我的电脑成精了?
更诡异的是,pip show itsdangerous 一开始显示 1.1.0,后来干脆说"没安装"!
第二重迷惑:路径对不上
运行 pip --version,我以为一切正常:
csharp
pip 25.0.1 from c:\projects\shorturl_service\venv\...
等等!我的项目叫 multi-llm,怎么 pip 路径是 shorturl_service?
原来......我当初是直接复制了另一个项目 shorturl_service,改名成 multi-llm 的!
而虚拟环境 venv 是跟着文件夹一起复制过来的 。虽然文件夹名字变了,但 venv 内部所有路径、激活脚本、解释器引用仍然指向"逻辑上的旧项目"。
PowerShell 虽然显示 (venv),但底层用的还是 shorturl_service 的环境!
这就导致:
- 我在
multi-llm目录下操作 - 但
pip和python实际作用于shorturl_service的 venv - 两个项目的包混在一起,版本冲突爆炸
第三重迷惑:包"幽灵式"存在
即使我卸载了 itsdangerous,Python 依然能 import 它!
原因可能是:
- 通过
pip install -e可编辑安装过 - 手动复制过源码到
site-packages .egg-link或easy-install.pth残留
pip 根本不知道这个包的存在,但 Python 导入系统能找到它------典型的"游离包"。
终极解决方案:彻底重建虚拟环境
✅ 正确操作步骤:
powershell
# 1. 退出当前(错误的)环境
deactivate
# 2. 删除复制来的旧 venv(罪魁祸首!)
Remove-Item -Recurse -Force venv
# 3. 创建全新的虚拟环境
python -m venv venv
# 4. 激活
.\venv\Scripts\Activate.ps1
# 5. 重装依赖
pip install -r requirements.txt
✅ 适配新版 itsdangerous(>=2.0)
既然用新版,就按新版 API 写:
python
# 生成 token
s = URLSafeTimedSerializer(secret_key, salt='confirm-email')
token = s.dumps({'confirm': self.id}, expiration) # 注意:expiration 是 dumps 的参数!
# 验证 token
data = s.loads(token, max_age=expiration) # 注意:用 max_age!
血泪教训总结
| 错误做法 | 正确做法 |
|---|---|
| 复制整个项目文件夹(含 venv)并改名 | 只复制源码,重新创建 venv |
相信 pip list 而不验证运行时版本 |
用 python -c "import pkg; print(pkg.__version__)" 确认真实版本 |
忽略 pip --version 的路径 |
每次激活 venv 后,检查路径是否匹配当前项目 |
| 试图"修复"混乱的环境 | 直接删除 venv 重建,省时省力 |
最后忠告
虚拟环境(venv)不是普通文件夹,它是和绝对路径绑定的"活体" 。
复制它,就像克隆一个人却不更新他的身份证号------迟早出问题。
从此以后,我的 .gitignore 里永远有这一行:
venv/
而新建项目的第一步,永远是:
bash
python -m venv venv
------谨以此文,祭奠我浪费的 3 小时 debug 时间。