首页
/ Poetry 基础使用指南:项目创建、依赖声明、虚拟环境与锁文件工作流

Poetry 基础使用指南:项目创建、依赖声明、虚拟环境与锁文件工作流

2026-09-05 18:13:46作者:殷蕙予

本篇指南基于 Poetry 官方文档 docs/basic-usage.md 展开,以安装 pendulum 这个 datetime 库为示例,系统讲解从创建项目骨架、指定 Python 版本、声明并添加依赖,到使用虚拟环境、理解 poetry.lock 锁定机制与依赖更新策略的完整基础工作流。读完本文,你将能够独立完成一个 Poetry 管理项目的初始化、依赖安装与日常维护,并能对照仓库源码理解每一步背后的实现机制。

poetry new 到标准项目骨架

创建新项目的起点是 poetry new 命令:

poetry new poetry-demo

这会在 poetry-demo 目录下生成如下结构:

poetry-demo
├── pyproject.toml
├── README.md
├── src
│   └── poetry_demo
│       └── __init__.py
└── tests
    └── __init__.py

其中最核心的文件是 pyproject.toml,它编排整个项目及其依赖。当前版本生成的文件内容如下:

[project]
name = "poetry-demo"
version = "0.1.0"
description = ""
authors = [
    {name = "Sébastien Eustace", email = "sebastien@eustace.io"}
]
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
]

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

几个关键约定值得注意:

  • 包位置约定:Poetry 默认假设项目根目录下存在一个与 project.name 同名的包。若你的包不在该位置,需通过 tool.poetry.packages 显式指定包及其位置。
  • MANIFEST.in 的替代物:传统 setuptools 的 MANIFEST.inproject.readmetool.poetry.includetool.poetry.exclude 取代;其中 tool.poetry.exclude 会隐式地继承 .gitignore 的内容。
  • 完整的项目格式说明见 pyproject 章节

源码视角:new 命令如何生成骨架

从源码看,new 命令直接复用 init 命令的核心逻辑。NewCommandsrc/poetry/console/commands/new.py 中定义,它继承自 InitCommand,并额外提供以下选项:

选项 说明
-i, --interactive 允许交互式地逐项填写项目配置
--name 设置生成的包名
--src 使用 src 布局(现已是默认行为,该选项已标记为即将移除)
--flat 使用扁平布局(包直接位于项目根目录)
--readme 指定 README 格式,默认为 md

其中布局选择由 handle() 完成:

return self._init_pyproject(
    project_path=path,
    allow_interactive=self.option("interactive"),
    layout_name="standard" if self.option("flat") else "src",
    readme_format=self.option("readme") or "md",
    allow_layout_creation_on_empty=True,
)

也就是说,src 布局是当前默认——这解释了为什么示例骨架中包位于 src/poetry_demo 之下。src 布局的定义非常直接,见 src/poetry/layouts/src.py

class SrcLayout(Layout):
    @property
    def basedir(self) -> Path:
        return Path("src")

另外还有一个实用细节:如果目标目录已存在且非空new 命令会直接报错并提示改用 poetry init(见 new.py L75-L79):

Destination <path> exists and is not empty. Did you mean `poetry init`?

这一设计与下文"初始化既有项目"的用法正好衔接。

requires-python 指定 Python 版本

注意:与某些其他工具不同,Poetry 不会自动为你安装 Python 解释器。如果你要在包内直接以脚本/应用形式运行 Python 文件,必须自行准备(bring your own)解释器。

Poetry 要求你显式声明打算支持的 Python 版本范围,其"universal locking"(通用锁定)会保证项目在所有支持的 Python 版本上都可安装,且所有依赖都声明兼容这些版本。再次强调:设置 Python 版本只是声明支持范围,而不是触发解释器安装。

例如在 pyproject.toml 中:

[project]
requires-python = ">=3.9"

表示允许任何大于等于 3.9.0 的 Python 3 版本。当你运行 poetry install 时,系统上必须已存在一个满足该约束的 Python 解释器可用,Poetry 不会替你安装。

从源码看,当你通过 poetry init / poetry new 走交互式流程且未手动指定 Python 版本时,默认值会基于当前系统优先解释器自动生成,见 init.py L171-L177

python = self.option("python")
if not python:
    config = Config.create()
    python = (
        ">="
        + Python.get_preferred_python(config, self.io).minor_version.to_string()
    )

即生成的默认约束形如 >=3.x(x 为当前解释器的次版本号),之后再交给用户交互确认。

在既有项目中初始化:poetry init

如果目录里已经有代码,不想从零创建项目,可以直接在目录中交互式生成 pyproject.toml

cd pre-existing-project
poetry init

init 命令的完整选项定义于 src/poetry/console/commands/init.py

选项 说明
--name 包名(默认取所在目录名的小写形式)
--description 包描述
--author 作者名(非交互时会尝试读取 git 的 user.name / user.email
--python 兼容的 Python 版本约束
--dependency 可重复使用,如 requests:^2.10.0requests=2.11.1
--dev-dependency 开发依赖,格式同上
-l, --license 许可证

交互式流程的核心逻辑在 _init_pyproject() 中,值得了解几个行为:

  • 默认值推导:包名默认是 project_path.name.lower(),版本默认为 0.1.0,作者会尝试从 GitConfig() 读取 user.nameuser.emailL129-L156)。
  • 依赖识别:输入裸包名时,Poetry 会通过仓库池搜索(self._get_pool().search(...))列出候选(默认来自 PyPI),并让你选择或补全完整包名;输入 git URL、URL、文件路径或目录等带来源的依赖则直接采用(L305-L439)。
  • 自动版本选择:如果只给包名不给版本约束,Poetry 会调用 VersionSelector.find_best_candidate 找到最佳版本,并自动生成 caret 约束 ^x.y.zinit.py L441-L460):
selector = VersionSelector(self._get_pool())
package = selector.find_best_candidate(
    name, required_version, allow_prereleases=allow_prereleases, source=source
)
...
return package.pretty_name, f"^{version.to_string()}"

生成的最终内容会先打印出来,等你确认后才写入 pyproject.toml,写入失败或已有冲突的 build-system 时会中止(L104-L117)。

两种运行模式:package mode 与 non-package mode

Poetry 有两种操作模式:

  • package mode(默认):适合需要把项目打包成 sdist/wheel 并发布到包索引的场景。此模式下 nameversion 等打包必需元数据是强制的;运行 poetry install 时项目本身会以 editable 模式 安装。
  • non-package mode:只用 Poetry 管理依赖、不做打包分发:
[tool.poetry]
package-mode = false

此模式下 nameversion 变为可选,项目无法构建发行版或发布到索引;运行 poetry install 时 Poetry 不会安装项目本身,只安装其依赖(等效于 poetry install --no-root)。各字段在 package mode 下的必填要求见 pyproject 章节

从源码可以印证这条规则:install 命令 在执行完依赖安装后,通过 no-root 选项或 poetry.is_package_mode 属性判断是否还要安装当前项目:

if self.option("no-root") or not self.poetry.is_package_mode:
    return 0

install 命令的帮助文本 也明确写明了这一用法:

By default, the above command will also install the current project. To install only the dependencies and not including the current project, run the command with the --no-root option. If you want to use Poetry only for dependency management but not for packaging, you can set the "package-mode" to false in your pyproject.toml file.

另外,install.py L209-L218 在根项目安装失败时也会给出排障提示:用 --no-root、设置 package-mode = false,或检查是否需要设置 packages

声明与添加依赖

手动声明

[project] 段的 dependencies 数组中按"包名 + 版本约束"的字符串形式声明:

[project]
# ...
dependencies = [
    "pendulum (>=2.1,<3.0)"
]

Poetry 依据这些信息在包"仓库"中搜索正确的文件集合——即你在 tool.poetry.source 段注册的仓库,未注册任何额外仓库时默认使用 PyPI。

poetry add 自动添加

不想手动改 pyproject.toml 时,可以直接:

$ poetry add pendulum

它会自动找到合适的版本约束,并同时安装该包及其子依赖(即声明 + 解析 + 安装一步完成)。

Poetry 支持丰富的 依赖规范语法,包括 caret(^)、tilde(~)、通配符、不等号以及多重约束。本仓库自身的 pyproject.toml 就是一个真实示例,可以看到不等号约束、环境标记与 git 来源混用的写法:

dependencies = [
    "poetry-core @ git+https://github.com/python-poetry/poetry-core.git",
    "build (>=1.2.1,<2.0.0)",
    "tomli (>=2.0.1,<3.0.0) ; python_version < '3.11'",
    "xattr (>=1.0.0,<2.0.0) ; sys_platform == 'darwin'",
    ...
]

使用虚拟环境

默认情况下,Poetry 在 {cache-dir}/virtualenvs 下创建项目的虚拟环境。cache-dir 可通过 Poetry 配置修改(见 configuration 文档的 cache-dir 一节)。若希望虚拟环境位于项目目录内,可使用 virtualenvs.in-project 配置项。

默认配置从哪里来

这些默认值在源码中一目了然,见 src/poetry/config/config.py

default_config: ClassVar[dict[str, Any]] = {
    "cache-dir": str(DEFAULT_CACHE_DIR),
    "data-dir": str(data_dir()),
    "virtualenvs": {
        "create": True,
        "in-project": None,
        "path": os.path.join("{cache-dir}", "virtualenvs"),
        ...
    },
}

DEFAULT_CACHE_DIR 本身基于 platformdirsuser_cache_path("pypoetry") 计算,且数据目录可通过环境变量 POETRY_HOME 覆盖(见 src/poetry/locations.py)。虚拟环境路径的最终解析逻辑在 Config.virtualenvs_path:未显式设置 virtualenvs.path 时回退到 {cache-dir}/virtualenvs

关于外部管理的虚拟环境,文档中有专门说明:

外部虚拟环境管理:Poetry 会检测并尊重一个已外部激活的既有虚拟环境。这是一个强大的机制,可视为 Poetry 内置简化环境管理的替代方案。要在其中获益,只需用你习惯的方式或工具激活虚拟环境,然后再运行任何需要操作环境的 Poetry 命令即可。

在 conda 等场景下无需再依赖 poetry run:既然你已经激活了目标环境,下面这些命令应当输出相同的 python 路径:

conda activate your_env_name
which python
poetry run which python
eval "$(poetry env activate)"
which python

poetry run 执行命令

运行脚本:

poetry run python your_script.py

运行命令行工具(如 pytestblack)同理:

poetry run pytest

poetry run 的实现见 src/poetry/console/commands/run.py

def handle(self) -> int:
    args = self.argument("args")
    script = args[0]
    scripts = self.poetry.local_config.get("scripts")

    if scripts and script in scripts:
        return self.run_script(scripts[script], args)

    try:
        return self.env.execute(*args)
    except FileNotFoundError:
        self.line_error(f"<error>Command not found: <c1>{script}</c1></error>")
        return 1

有两点值得注意:

  1. 优先解析 [tool.poetry.scripts]:如果第一个参数是 pyproject.toml[tool.poetry.scripts] 定义的脚本名,会走 run_script() 分支执行对应的可调用对象,否则直接在项目环境中执行该命令。
  2. 未安装脚本的告警:入口点脚本若尚未通过 poetry install 安装为可执行文件,run_script() 会警告 sys.argv[0] 可能不正确,并提示先运行 poetry installrun.py L90-L98)。

虚拟环境的激活方式(poetry env activate 等)详见 managing-environments 文档

版本约束与仓库查找机制

示例中请求 pendulum 时使用的版本约束是 >=2.1.0 <3.0.0,含义为"大于等于 2.1.0 且小于 3.0.0 的任何版本"。关于版本的详细语义、版本间关系及各类依赖写法,请参阅 Dependency specification

关于 Poetry 如何下载正确的文件,文档给出了查找规则:

  1. Poetry 先取你请求的包名,在通过 repositories 键注册的各仓库中搜索;
  2. 若没有注册额外仓库,或已注册仓库中找不到该包名,则回退到 PyPI;
  3. 找到包后,再尝试为你的版本约束寻找最佳匹配。

安装依赖:poetry install 的两条路径

安装项目依赖只需要:

poetry install

运行后会发生两种情形之一。

没有 poetry.lock

如果从未运行过该命令、也不存在 poetry.lock 文件,Poetry 会解析 pyproject.toml 中列出的所有依赖并下载最新版本的文件。安装完成后,它会把下载的所有包及其精确版本写入 poetry.lock,将项目锁定到这些特定版本。应当把 poetry.lock 提交到项目仓库,使所有参与者锁定在相同的依赖版本上。

poetry.lock

若运行 poetry installpoetry.lockpyproject.toml 同时存在(说明你或队友此前跑过 install 并提交了锁文件),Poetry 仍会解析 pyproject.toml 中列出的依赖,但使用 poetry.lock 中列出的精确版本 安装,保证团队所有成员的包版本一致。因此你的依赖未必都是最新可用版本——锁文件中记录的版本创建后可能有新版本发布。这是有意设计,确保项目不会因为依赖的意外变化而损坏。

此外还有一层校验机制:锁文件中记录了与 pyproject.toml 对应的哈希,Locker 会检查锁文件是否仍与当前 pyproject.toml 同步;同时它也会校验锁文件自身的版本兼容性,例如 locker.py L364-L382 中对版本不兼容锁文件直接要求用 poetry lock 重新生成。这与文档中"当 poetry.lockpyproject.toml 不同步时,install 会显示 Warning"的说明相对应。

poetry.lock 提交到版本控制:应用开发者 vs 库开发者

作为应用开发者:提交 poetry.lock 以获得更可复现的构建。其价值在于:任何人搭建该项目时都会使用与你完全一致的依赖版本——CI 服务器、生产机器、团队其他成员跑在同一套依赖上,从而降低"只影响部分部署环境的 bug"的发生概率。即使你独自开发,半年后重装项目时也可以确信依赖仍然可用,尽管这期间依赖可能发布了大量新版本。

注意:即使你的 pyproject.toml 中包含推荐的 [build-system] 段,你也可以用 pip install -e . 之类的命令把项目及其依赖安装进虚拟环境。但 pip 不会使用锁文件来决定依赖版本,因为 poetry-core 构建系统面向的是库开发者(见下节)。

作为库开发者:情况更复杂。你的用户是应用开发者,你的库会运行在你无法控制的环境中——应用会忽略你库的锁文件,可以使用任何满足你 pyproject.toml 约束的版本,通常是最新的兼容版本。如果库的 poetry.lock 落后于某些会"搞坏"用户环境的新依赖版本,你很可能最后一个得知。

简单的规避方式是直接省略 poetry.lock,但这会牺牲一定的可复现性与性能:没有锁文件时,测试失败的原因可能不只是代码变更,还可能是未被注意的库版本更新;而且每次安装前 Poetry 都需要先执行锁定,依赖较多时锁定时长可观。若不愿放弃可复现性与性能,可以考虑定期刷新 poetry.lock,保持更新以降低用户端突然出错的概率。

只安装依赖:--no-root

当前项目默认以 editable 模式 安装。若只想安装依赖、不安装项目本身:

poetry install --no-root

该选项在源码中的定义见 install.py L29-L31"Do not install the root package (the current project)."

poetry update 更新依赖

如前所述,poetry.lock 会阻止你自动获得依赖的最新版本。要升级到最新版本,使用 update 命令:

poetry update

它会拉取符合 pyproject.toml 约束的最新匹配版本,并用新版本更新锁文件(等效于删除 poetry.lock 后重新运行 install)。

最后再提醒一次同步警告:poetry.lockpyproject.toml 不同步时,执行 install 类命令 Poetry 会显示 Warning——这通常意味着有人改了依赖声明却没有重新锁定,此时运行 poetry lockpoetry update 恢复同步即可。


小结:本指南覆盖了 Poetry 日常使用的核心闭环——poetry new / poetry init 创建骨架 → requires-python 声明解释器支持范围 → package / non-package 两种模式取舍 → 手动或 poetry add 声明依赖 → poetry run 在虚拟环境中执行 → poetry install 借助 poetry.lock 保证团队版本一致 → poetry update 受控升级。每一步均可在当前仓库的 docs/cli.mddocs/pyproject.mddocs/dependency-specification.md 与对应命令源码中进一步查证。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384