Conda+Poetry+pytest进行Python项目管理

注:以下文章有ChatGPT辅助生成

1. 为什么需要项目管理

1.1 大型 Python 项目的常见痛点

1.1.1 包管理地狱(Dependency Hell)

  • 版本冲突pip install A 后发现 B 坏了;升级一个库,另一个库的 API 变了。
  • 跨平台不一致:Windows 上装得好好的,Linux 服务器上编译不过;macOS 上还要 Homebrew。
  • 科学计算/深度学习库难装numpy / pytorch / opencv / xgboost… 这些库往往涉及 C/C++、CUDA、MKL,pip 纯 Python 轮子经常行不通。

1.1.2 业务逻辑引用混乱

  • src 乱放,import 乱飞from utils import func 到处乱引,重构就爆炸。
  • 测试/脚本无法正确引用项目代码sys.path.append('../src') 这种“土法上马”长期看不可维护。

1.1.3 与 AI 协作的风险和难度

  • AI 生成代码可能随手 pip install 或改动 pyproject:不受控。
  • AI 误改项目结构/依赖:比如把业务代码塞在根目录、删除 __init__.py、或在 notebook 里直接 import 本地文件。
  • 环境不可复现:AI 生成的说明往往缺少锁定版本或构建脚本,团队协同困难。
  • AI上下文限制:由于AI的上下文长度限制,使代码应尽量拆细到多个文件,这使得工程的文件量增加很多。有必要使这些文件能以一种经由实践检验的好方式来组织,使人和AI都能很好驾驭。

1.2 结论:需要一套“硬件 + 软件”双保险

  • Conda:解决“硬件层/二进制层”的依赖(Python 解释器版本、CUDA、MKL、C 库等)。
  • Poetry:解决“软件层/纯 Python 层”的依赖与打包(锁定版本、发布、依赖解析)。
  • 规范的项目结构:让 IDE、测试框架、CI/CD、AI 代码生成都能遵守共同的路径与规则。

2. 工具介绍

2.1 Conda:环境与二进制依赖管理器

  • 特点
    • 创建隔离环境(可指定 Python 版本)。
    • 安装预编译的科学计算大包,兼容 Windows/Linux/macOS。
    • 支持非 Python 依赖(如 cuda, ffmpeg, graphviz)。
  • 适用场景:数据科学 / 深度学习 / 需要复杂底层依赖的项目。

2.2 Poetry:现代化 Python 依赖 & 构建管理器

  • 特点
    • 使用 pyproject.toml 管理依赖和项目元信息。
    • 自动生成 poetry.lock,确保依赖版本一致。
    • 提供 poetry build / poetry publish,方便打包发布。
    • 与 pip/venv 兼容,也可以“接管”已有环境。
  • 适用场景:任何需要:版本锁定、可发布、团队协作、CI/CD 的 Python 项目。

2.3 为什么组合使用?

需求/能力CondaPoetry组合效果
安装 CUDA / MKL / C 库✅(Conda 负责)
锁定纯 Python 依赖版本✅(Poetry 负责)
构建 & 发布 PyPI 包
跨平台一致性
团队协作 / CI 可复现性

2.4 Pytest:强大且灵活的 Python 测试框架

  • 特点:
    • 支持简单函数式测试,也支持类和夹具(fixture)的结构化测试。
    • 自动发现以 test_ 开头的函数 / 文件 / 类,易于组织测试代码。
    • 提供强大的断言重写机制,错误信息清晰、可读。
    • 支持参数化、跳过、预期失败等高级测试控制。
    • 丰富的插件生态(如 pytest-covpytest-xdist),便于扩展和集成。
  • 适用场景:适用于任何需要高可读性、易扩展、适配 CI 流程的 Python 测试项目,尤其适合中大型项目的单元测试与集成测试需求。

3. 完整项目示例:从 0 到 1

我们来搭建一个最小可运行的示例项目:my_project

3.1 Conda 环境搭建

1)创建环境:

# 创建名为 qs的 Conda 环境并安装基础包
conda create -n qspython=3.11 numpy pandas -y
conda activate qs

2)生成 environment.yml(可选,但推荐)

进入项目根目录并运行

conda env export --from-history > environment.yml

--from-history 只导出你手动安装的包,避免导出一大堆依赖树。

示例 environment.yml

name: qs
channels:
  - conda-forge
dependencies:
  - python=3.11
  - numpy
  - pandas
  - pip  # 让你还能用 pip 安装少量 PyPI 包

3.2 Poetry 环境搭建

1)在 Conda 环境中安装 Poetry(推荐用 pip 安装到当前环境):

pip install poetry

2)让 Poetry 使用当前 Conda 环境(关键):

poetry config virtualenvs.create false

这行的意思是:Poetry 不再创建自己的 venv,而是复用你激活的 Conda 环境。

3)初始化 pyproject.toml:

poetry init
# 按提示填写:项目名、版本、依赖等,也可以后续手动编辑

或者直接编辑一个最小示例:

[tool.poetry]
name = "quant-strategy-v2"
version = "0.1.0"
description = ""
authors = ["Raytto <306483372@qq.com>"]
readme = "README.md"
packages = [{ include = "qs", from = "src" }]
# optional: if you later publish to PyPI
# license = "MIT"

[tool.poetry.dependencies]
python = "^3.11"

[tool.poetry.group.dev.dependencies]
pytest = "^8.4.1"

[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

如果指定了readme = "README.md",则需要在项目目录创建一个README.md

"# quant-strategy-v2" | Out-File -Encoding utf8 README.md

如果指定了 packages = [{ include = "qs", from = "src" }],则需要创建一下目录和文件:

├── src/
│   └── qs/
│       ├── __init__.py
│       └── main.py
│       └── core.py

4)安装依赖:

poetry install
# 或者新增依赖:poetry add requests
# 新增开发依赖:poetry add --group dev pytest

3.3 项目文件结构

my_project/
├── environment.yml              # Conda 环境定义
├── pyproject.toml               # Poetry 配置
├── poetry.lock                  # Poetry 锁文件(自动生成)
├── README.md
├── src/
│   └── my_package/
│       ├── __init__.py
│       └── main.py
│       └── core.py
├── tests/
│   └── test_core.py
└── notebooks/
    └── demo.ipynb

3.4 最小可运行实例

src/qs/core.py

def add(a: int, b: int) -> int:
    return a + b

tests/test_core.py

from qs.core import add

def test_add():
    assert add(1, 2) == 3

dev环境下安装 pytest :

poetry add --dev pytest

运行测试:

poetry run pytest -q

运行模块:

python -c "from qs.core import add; print(add(3, 4))"

3.5 每次重新打开工程时需要做什么?

  1. 打开终端,激活 Conda 环境:conda activate qs
  2. (首次)同步 Poetry 依赖:poetry install
  3. 运行测试 / 脚本 / notebook。

注:常规开发周期里只要记得 conda activate,Poetry 的安装只在依赖更新后需要执行一次。

3.6 从 Git 克隆后如何准备环境?

  1. 克隆代码:git clone git@github.com:yourname/my_project.git cd my_project
  2. environment.yml 创建环境
    • 如果提供了):conda env create -f environment.yml conda activate my_project
    • 如果没有 environment.yml,你也可以手动创建:conda create -n my_project python=3.11 -y conda activate my_project
  3. 安装 Poetry(若环境首次使用):pip install poetry poetry config virtualenvs.create false
  4. 安装项目依赖:poetry install # 会根据 poetry.lock 安装精确版本
  5. 运行测试验证:pytest -q

4. 最佳实践与小贴士

  • Conda 安装“硬依赖”(科学计算、GPU、系统库),Poetry 管理“软依赖”(纯 Python 包)
  • 只在必要时使用 pip
    • Conda 找不到的包,用 pip install;但尽量仍通过 Poetry (poetry add) 来锁定版本。
  • 保持 src/ 布局
    • packages = [{ include = "my_package", from = "src" }]
    • 测试只需 from my_package import xxx,无需改 sys.path
  • 避免把业务代码放在 notebooks 中:notebook 应调用包函数,确保逻辑可测试、可复用。
  • 把 AI 当实习生,用规范“约束”它
    • 给 AI 明确的项目结构说明和依赖管理原则。
    • 设定自动化检查(pre-commit、CI)来防止不合规代码进入主分支。
  • 在 CI/CD 中:
    • conda-lock 或者导出 environment.yml 来固定 Conda 版本。
    • poetry install --no-root 安装依赖;需要时 poetry build + pip install dist/*.whl

5. 其他相关实践

5.1 把当前环境注册为ipykernel

方便notebook使用.

在对应conda虚拟环境中

vpoetry config virtualenvs.create false   # 关键:复用当前 Conda 环境

# 3. 安装项目依赖
poetry install
c # 若还没装 ipykernel

# 4. 注册为 Jupyter Kernel
poetry run python -m ipykernel install --user --name qs --display-name "my_quant_strategy"

发表评论