You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从嵌套Git子模块安装独立Python包(不修改子模块)

问题描述

项目结构

project-dir
├── pyproject.toml
├── bin
│   └── app.py
├── main_pkg
│   └── __init__.py
├── setup.py
└── tools (git子模块)
    ├── setup.py
    ├── a_pkg
    │   └── __init__.py
    ├── b_pkg
    │   └── __init__.py
    └── c_pkg
        └── __init__.py

需求

将main_pkg、a_pkg、b_pkg、c_pkg直接安装到.venv/lib/python3.10/site-packages/根目录,同时安装子模块tools的依赖,且不能修改git子模块(无写入权限)。

当前pyproject.toml配置

[build-system]
build-backend = 'setuptools.build_meta'
requires = ['setuptools >= 63.0.0']

[project]
name = "main_pkg"
version = "3.8.1a"
requires-python = ">3.10.1"

dependencies = [
    "aiohttp >= 3.7.4",
    "aiohttp_cors",
    "aiosql",
    "jsonschema",
    "requests",
    "sentry-sdk"
]

[project.optional-dependencies]
test = [
    "coverage",
    "hypothesis",
    "pytest"
]
dev = [
    "psycopg2-binary >= 2.9.5",
    "sqlalchemy >= 1.4.43",
    "black",
    "flake8",
    "Flake8-pyproject",
    "isort",
    "pre-commit",
    "python-dotenv",
    "vermin",
]

[project.scripts]
main = "bin.app:start"

[tool.setuptools]
include-package-data = true
package-dir = {main_pkg="main_pkg", a_pkg="tools/a_pkg", b_pkg="tools/b_pkg", c_pkg="tools/c_pkg"}

[tool.setuptools.packages.find]
where = [".", "tools"]
include = ["main", "a_pkg", "b_pkg", "c_pkg"]

问题现象

执行安装后,整个tools目录被完整安装到site-packages,还包含冗余的build目录:

.venv/lib/python3.10/site_packages/
├── main_pkg
│   └── __init__.py
├── tools
    ├── setup.py
    ├── build
    │   └── lib/* 
    ├── a_pkg
    │   └── __init__.py
    ├── b_pkg
    │   └── __init__.py
    └── c_pkg
        └── __init__.py

期望的安装结构

.venv/lib/python3.10/site_packages/
├── main_pkg
│   └── __init__.py
├── a_pkg
│   └── __init__.py
├── b_pkg
│   └── __init__.py
└── c_pkg
    └── __init__.py

尝试调整[tool.setuptools.packages.find]导致安装混乱,使用package-dir显式声明后目标包未被安装,询问遗漏的配置。


解决方案

1. 修正后的pyproject.toml配置

核心问题是package-dir与自动包发现的配置冲突,且未排除tools目录本身被打包。以下是正确配置:

[build-system]
build-backend = 'setuptools.build_meta'
requires = ['setuptools >= 63.0.0']

[project]
name = "main_pkg"
version = "3.8.1a"
requires-python = ">3.10.1"

dependencies = [
    "aiohttp >= 3.7.4",
    "aiohttp_cors",
    "aiosql",
    "jsonschema",
    "requests",
    "sentry-sdk",
    # 手动添加tools子模块setup.py中的依赖(因无权限修改子模块,需硬编码)
    # 示例:假设tools/setup.py的依赖是"requests>=2.25.0",请替换为实际内容
]

[project.optional-dependencies]
test = [
    "coverage",
    "hypothesis",
    "pytest"
]
dev = [
    "psycopg2-binary >= 2.9.5",
    "sqlalchemy >= 1.4.43",
    "black",
    "flake8",
    "Flake8-pyproject",
    "isort",
    "pre-commit",
    "python-dotenv",
    "vermin",
]

[project.scripts]
main = "bin.app:start"

[tool.setuptools]
include-package-data = true
# 根包目录映射为当前项目根,同时指定子模块内包的路径
package-dir = {"" = ".", "a_pkg" = "tools/a_pkg", "b_pkg" = "tools/b_pkg", "c_pkg" = "tools/c_pkg"}

# 明确列出要安装的包,避免自动发现误识别tools目录
packages = ["main_pkg", "a_pkg", "b_pkg", "c_pkg"]

# 排除tools目录下所有文件,防止被打包到site-packages
exclude-package-data = {"" = ["tools/*"]}

2. 配置说明

  • package-dir:"" = "."定义项目根为默认包路径,再单独映射子模块内的包,确保setuptools能准确定位源码。
  • packages:直接指定要安装的包名,替代自动发现机制,避免把tools目录(含setup.py)误识别为一个包。
  • exclude-package-data:完全排除tools目录的文件,阻止其被打包到site-packages。
  • 子模块依赖处理:因无法修改子模块的setup.py,需手动将tools/setup.py中install_requires的内容复制到主项目的dependencies列表,确保子模块依赖被安装。

3. 安装命令

执行可编辑安装或常规安装即可:

# 可编辑安装(方便开发)
pip install -e .
# 常规安装
pip install .

内容的提问来源于stack exchange,提问作者crowgers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:35:27