pyproject.toml は、Python のプロジェクトの名前、使うパッケージ(依存)、ビルドの道具、ほかの道具の設定を、1つのファイルにまとめて書く設定ファイルです。requirements.txt は、pip で入れるパッケージの一覧を書いたファイルで、書くものも、使われ方も違います。
この記事は、requirements.txt は見たことがあるが pyproject.toml は初めて見た、という人向けです。pyproject.toml の書き方、requirements.txt との違い、依存のグループ、ロックファイル、pip と uv での使い分け、requirements.txt からの移り方を、2026年10月10日時点の公式の資料と、pip 26.2.1・uv 0.13.0 を動かした結果をもとにまとめました。
- pyproject.toml は何を書くファイルで、どんな表(テーブル)があるのか
[build-system]と[project]の書き方(名前・版・依存・追加の機能・コマンド)- requirements.txt との違い(役割・読まれ方・標準かどうか)
- 依存のグループ(
[dependency-groups])と、ロックファイル(requirements.txt・uv.lock・pylock.toml)の位置づけ - pip と uv での入れ方の違いと、requirements.txt から pyproject.toml への移り方(uv)
この記事で確かめた版(2026年10月10日時点)
- 出力例: macOS(Apple Silicon)、Python 3.13.11、pip 26.2.1、uv 0.13.0。パッケージの版(requests 2.34.2 など)は、2026年10月10日に PyPI から取った版で、日が変わると変わります
- pip 26.2.1 は PyPI で2026年8月4日に載った最新版、uv 0.13.0 は2026年10月9日に出た最新版です
- uv の使い方の記事 Python の uv とは?使い方と pip・venv との違い【uv 0.12 対応】は、uv 0.12 系で書いています。
uv initが書く数字(uv_build の版など)は uv の版で変わるので、この記事の uv 0.13.0 の出力とは数字が違います - Windows のコマンドは、公式の文書のとおりに書いています。出力例はすべて macOS のものです
- 出力例のコマンドの先頭の
python -m pipは、仮想環境の Python で流したものです(仮想環境の場所は省いています)。長い出力は、途中を省いたものがあります
pyproject.toml とは?何を書くファイルか
最初に言葉を説明します。TOML は、設定を書くための書式の名前です。表(テーブル)は、[project] のように角かっこで名前を付けた、設定のまとまりです。依存は、プロジェクトが動くために必要な、ほかのパッケージです。
pyproject.toml は設定ファイル
PyPA(Python のパッケージの道具を保守する作業グループ)の手引きは、pyproject.toml を、パッケージを作る道具や、リンター・型チェッカーなどのほかの道具が使う設定ファイルと説明しています。リンターは、コードの書き方の問題点を調べる道具、型チェッカーは、型の食い違いを調べる道具です。
PyPA の用語集は、「どの道具にも依らない、プロジェクトの仕様のファイル」と説明し、PEP 518 で決まったと書いています。PEP は、Python の仕様や改善を提案する文書のことです。
表は何があるか
pyproject.toml の仕様で決まっている表は、[build-system]・[project]・[tool] の3つです。ほかの名前は将来のために取ってあり、道具の設定は [tool] に書く決まりです。
一方、[dependency-groups] は、別の仕様(Dependency Groups)が決めた表で、pip や uv も読みます。そのため、この記事では「表は3つ」とは言わず、仕様ごとに分けて説明します。
| 表 | 何を書くか | 決めている仕様 |
|---|---|---|
[build-system] | ビルドの道具(ビルドバックエンド)と、ビルドに要るもの。手引きは書くことを強く勧めています | pyproject.toml の仕様 |
[project] | 名前・版・依存など、プロジェクトの基本の情報。ほとんどのビルドバックエンドが使う形です | pyproject.toml の仕様 |
[tool] | 道具ごとの設定。[tool.uv] のように、道具の名前の小さな表に書きます | pyproject.toml の仕様 |
[dependency-groups] | 開発の中だけで使う依存などのグループ | Dependency Groups の仕様 |
なぜ生まれたのか
PEP 518 は、setup.py の行き詰まりを理由に挙げています。setup.py は、動かさないと何が要るか分からないのに、動かすにはそれが要る、という状態でした。そこで、ビルドに要るものを、決まった場所に、動かさずに読める形で書くことにしました。
形式に TOML を選んだ理由も PEP 518 に書かれています。JSON と違って人が扱いやすく、configparser と違って十分に柔軟で、標準があり、YAML と違って複雑すぎない、というものです。
仕様の歩みは、PyPA の仕様のページの履歴に次のようにあります。
- 2016年5月: PEP 518 で、
[build-system]と[tool]が決まった - 2020年11月: PEP 621 で、
[project]が決まった - 2024年12月: PEP 639 で、license の書き方が変わった
PEP 621・735・751 のページには、歴史的な文書で、いまの正式な仕様は PyPA の仕様のページにある、と出ています。この記事の「いまの書き方」は、PyPA の仕様と手引きを出典にしています。
新しいプロジェクトでは [project] を使います。手引きは、setup.py は、C 言語の拡張を作るなど、プログラムで設定する必要があるときだけ残す、と書いています(setup.cfg と setup.py の形も、まだ有効です)。
古い記事で [tool.poetry] という書き方を見ることがあります。Poetry は、2.0(2025年1月5日)より前は [project] を使わず [tool.poetry] に書いていて、2.0 からは両方を扱います。setuptools も、[project] と、古い setup.cfg・setup.py の両方を扱います。
pyproject.toml の書き方(build-system・project・tool)
pip で試すために手で書いた、pyproject.toml の例です(pip 26.2.1 で pip install .、pip install --group dev、pip install ".[cli]" が通ることを確かめました)。src/hello_pyproj/__init__.py も置いています。
[build-system]
requires = ["hatchling >= 1.26"]
build-backend = "hatchling.build"
[project]
name = "hello-pyproj"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
"requests>=2.32",
]
[project.optional-dependencies]
cli = ["rich"]
[dependency-groups]
dev = ["pytest"]
上から順に、[build-system]、[project](依存と、追加の機能 cli)、[dependency-groups] です。
[build-system]:ビルドの道具を決める
ビルドは、プロジェクトのソースから、配る形(sdist や wheel)を作ることです。それを担うライブラリをビルドバックエンドといいます。例は、hatchling・setuptools・flit-core などです。ビルドを頼む側(フロントエンド)の例は、pip と build です。
[build-system] には、2つを書きます。
build-backend: 使うビルドバックエンドrequires: ビルドに要るもの(ふつうはビルドバックエンドのパッケージだけ)。版の条件も付けられます(例:requires = ["setuptools >= 61.0"])
ふつうは、選んだビルドバックエンドの文書が勧める値をそのまま写します。PyPA の手引きにある例は、次のとおりです。
| ビルドバックエンド | requires | build-backend |
|---|---|---|
| Hatchling | ["hatchling >= 1.26"] | "hatchling.build" |
| setuptools | ["setuptools >= 77.0.3"] | "setuptools.build_meta" |
uv のビルドバックエンド(uv_build)は、requires の数字が uv の版で変わるので、数字は写さず、uv init が書いた値を使います。手元の uv 0.13.0 の uv init は、次の2行を書きました。
[build-system]
requires = ["uv_build>=0.13.0,<0.14.0"]
build-backend = "uv_build"
uv 0.13.0 の変更履歴は、uv_build の設定に互換を壊す変更は無いが、uv_build に上限を付けているなら 0.13 を許すように直す(例: uv_build>=0.13.0,<0.14)、と書いています。
[build-system] は書かないといけないのかは、読む資料で言い方が違います。
- PyPA の手引きは、
[build-system]を強く勧めています。どのビルドバックエンドでも、いつも書くように、という注もあります - pyproject.toml の仕様は、道具は
[build-system]があることを求めるべきではない、と書いています。pyproject.toml は、ビルド以外の設定だけを入れるのにも使えるからです。無いときは、仕様で決めた既定の値が使われます - 手元の uv 0.13.0 は、
uv initでは[build-system]を書き、uv init --no-packageでは書きませんでした
uv の文書は、ビルドに要るが動かすのには要らないものを、build-system.requires に書くと説明しています(PEP 518 に従う書き方)。
[project]:名前・版・依存を書く
[project] で、固定の値として必ず書くのは name だけです。version も必須ですが、固定で書くか、dynamic(あとでビルドバックエンドが埋める項目の一覧)に入れるかを選べます。ほかの項目は、書かなくてもかまいません。
[project] に書ける項目は、次の20個です。
authors・classifiers・dependencies・description・dynamic・entry-points・gui-scripts・import-names・import-namespaces・keywords・license・license-files・maintainers・name・optional-dependencies・readme・requires-python・scripts・urls・version
このうち、よく使う項目を見ます。細かい書き方は、PyPA の手引き(Writing your pyproject.toml)にあります。
name と version
name は、PyPI(Python のパッケージの置き場)での名前です。必須で、dynamic にできないのはこの項目だけです。使える文字は、ASCII の英字・数字・_・-・. で、先頭と末尾には _・-・. を置けません。名前の比べ方は、大文字小文字を区別せず、_・-・. の並びは同じとみなします(例: cool-stuff は、Cool-Stuff・cool.stuff・COOL_STUFF でも指せます)。
version は、dynamic = ["version"] にして、コード内の __version__ や Git のタグから埋めることも多い、と手引きは書いています。dynamic に入れた項目は、ビルドバックエンドが埋める決まりで、埋め方は各ビルドバックエンドの文書にあります。
dependencies:依存の一覧
dependencies は、依存の文字列の配列です。1つずつが、依存の書き方(dependency specifier)に従います。ここに書いたものは、必ず入れる候補になります(環境の条件で外れることはあります)。手引きの例から抜き出すと、次のような形です。
dependencies = [
"httpx",
"django>2.1; os_name != 'nt'",
"django>2.0; os_name == 'nt'",
]
; の後ろは環境の条件(マーカー)で、条件に合う環境でだけ入れる候補になります。
版の条件は、次の演算子で書きます。, は「かつ」の意味です。
| 演算子 | 意味 |
|---|---|
== | 一致 |
!= | 除く |
>= <= | 以上・以下 |
> < | より大きい・未満 |
~= | 互換のある版 |
=== | 文字どおり一致 |
~= の例は、~= 2.2 が >= 2.2, == 2.* と同じ、~= 1.4.5 が >= 1.4.5, == 1.4.* と同じ、です。
optional-dependencies:特定の機能のときだけ要る依存
optional-dependencies は、パッケージの特定の機能のときだけ要る依存を入れる場所です。キーごとに、extra(追加の機能)が1つできます。上の例の cli = ["rich"] なら、pip install ".[cli]" で rich も入ります(手元の pip 26.2.1 で、rich が入ることを確かめました)。
requires-python と scripts
requires-python は、対応する Python の最低の版です。上限(例: <= 3.10)を付ける前によく考えるよう、手引きに注があります。
[project.scripts] に書くと、入れたあとにコマンドとして使えます。手引きの例は、spam-cli = "spam:main_cli" を書くと、spam-cli というコマンドができる、というものです。uv 0.13.0 の uv init が書く hello-uv = "hello_uv:main" も、同じ表です。
license
license は、SPDX のライセンス式の文字列で書きます(例: license = "MIT")。以前の仕様にあった、file や text を持つ表の形は、非推奨です。ビルドで「license は dict/table であるべき」というエラーが出たら、ビルドバックエンドがまだ新しい形に対応していません。
新しい形に対応した版は、hatchling 1.27.0・setuptools 77.0.3・flit-core 3.12・pdm-backend 2.4.0・poetry-core 2.2.0・uv-build 0.7.19 です(手引きの表より)。
[tool]:道具ごとの設定
[tool] は、ビルドの道具に限らず、Python のプロジェクトに関わる道具が、設定を置く場所です。[tool.名前] を使えるのは、PyPI でその名前を持っている道具だけです(名前がぶつからないようにするため)。
uv なら、設定は [tool.uv] に書きます(既定で入れるグループを決める default-groups など)。依存の取得先を変えるときは [tool.uv.sources] に書きます。
pyproject.toml と requirements.txt の違いは?
requirements.txt とは
requirements.txt は、pip install で入れるものの一覧を書いたファイルです(pip の文書では「要件ファイル」と呼びます)。requirements.txt という名前にすることが多いだけで、名前は決まりではありません。中身は、pip install に渡す引数を、ファイルに並べたものです。
書けるものは、パッケージの名前だけ、版の条件つき、URL、ほかの要件ファイル(-r)、制約ファイル(-c)などです。# から後ろはコメントになります。
# 名前だけ
requests
# 版の条件つき
docopt == 0.6.1
入れるときは、Mac は python -m pip install -r requirements.txt です。Windows は、pip の文書にある py -m pip install -r requirements.txt を使います(Windows は手元では流していません)。
python -m pip install -r requirements.txt
pip の文書は、書き方の細かい部分は pip の内部と強く結びついていて、全部の書き方は pip が使うためのもの、ほかの道具が使うときは注意が要る、と注意しています。
FastAPI の環境づくりを説明した 【Python入門】Fast APIの実装手順とフレームワーク技術選定では、requirements.txt に fastapi と uvicorn の2行(版を書かず、名前だけ)を書いています。
違いを表で見る
PyPA は、setuptools の install_requires と要件ファイルを比べて説明しています。PEP 621 の対応表では、pyproject.toml の dependencies は、install_requires に当たります。次の表は、その説明と、pip の文書、PEP 735 をもとにまとめたものです。
| pyproject.toml | requirements.txt | |
|---|---|---|
| 書くもの | プロジェクトの基本の情報(名前・依存など)と、道具の設定 | pip で入れるものの一覧 |
| 依存の書き方 | 「抽象的」な要件。名前と版の条件だけで、どこから取るかは決めない | 版を固定した全部の一覧にすることが多い。--index-url などで取得先まで決めることも多い |
| 範囲 | 1つのプロジェクト | 環境まるごと(のことが多い) |
| 誰が読むか | pip などが、プロジェクトを入れるときに自動で読む | pip install -r で指定したときだけ使われる |
| 標準か | 仕様がある(PyPA) | 標準ではなく、pip へのオプションを渡すもの。pip 以外の道具に持っていきにくい |
| 複数の依存の組 | [project.optional-dependencies] や [dependency-groups] で、1つのファイルに書ける | 1つのファイルに1組だけ。開発用などは別のファイルにする |
表の補足です。
- PyPA の説明では、
install_requiresは「そのプロジェクトが正しく動くのに最低限要るもの」を書き、依存の依存まで固定して書くのは良いやり方ではありません。厳しすぎて、使う人が依存の更新の恩恵を受けられなくなるからです。分かっている下限・上限は書くのがよい、とも書かれています - 要件ファイルは、最低限ではなく、版を固定した全部の一覧にして、環境を同じに入れ直せるようにすることが多い、と PyPA は説明しています
- PEP 735 は、requirements.txt の弱みとして、名前の決まりが無く道具が見つけにくいこと、標準ではなく pip へのオプションであること、pip 以外の道具に持っていけないことを挙げています
- pip は、プロジェクトの依存を、メタデータ(ふつうは pyproject.toml か setup.py)から読みます。プロジェクトの中にある requirements.txt を探して読むことはしません
「requirements.txt は古い」「非推奨」といった言い方は、この記事で開いた公式の資料には見つかりませんでした。
手元で比べる
pyproject.toml の dependencies に書いたのは、"requests>=2.32"(下限だけ)の1行です。同じ環境(pytest と rich も入れたもの)で pip freeze を流して、requirements.txt に書き出すと、次のようになりました(pip 26.2.1・macOS)。
python -m pip freeze --exclude hello-pyproj > requirements.txt
certifi==2026.7.22
charset-normalizer==3.5.2
idna==3.20
iniconfig==2.3.1
markdown-it-py==4.2.0
mdurl==0.1.2
packaging==26.3
pluggy==1.6.0
Pygments==2.21.0
pytest==9.1.1
requests==2.34.2
rich==15.0.0
urllib3==2.8.0
requests の依存(certifi・charset-normalizer・idna・urllib3)や、pytest・rich とその依存まで、== で13行になりました。別の仮想環境に、この requirements.txt を pip install -r で入れると、同じ版が入りました。
pip freeze は、入っているものを要件ファイルの形で出します。pip の文書は、入っているものを報告するだけで、ロックファイルを計算するわけではない、と書いています。
--exclude hello-pyproj を付けた理由は、自分のプロジェクトを pip install . で入れた環境では、pip freeze がそのプロジェクトを版ではなく hello-pyproj @ file://… の形で出すからです(手元の出力)。外したいときは、pip freeze の --exclude <名前> を使います。
pip install -r pyproject.toml はできない
pip の -r が読めるのは、requirements.txt の形か、pylock.toml の形です。pyproject.toml を渡すと、手元の pip 26.2.1 ではエラー(終了コード 1)になりました。
$ python -m pip install -r pyproject.toml
ERROR: Invalid requirement: '[build-system]': Expected package name at the start of dependency specifier
[build-system]
^ (from line 1 of pyproject.toml)
pyproject.toml の依存を pip で入れる方法は、あとの「pip と uv での使い方の違い」で説明します。
依存のグループ([dependency-groups])とは?extra との違い
開発の中だけで使う依存を入れる場所
Dependency Groups(依存のグループ)は、pyproject.toml に書くけれど、ビルドしたパッケージのメタデータには入らない依存の一覧です。PyPA の仕様は、テストやリンターなど開発の中だけで使うものや、配らないプロジェクト(スクリプトの集まりなど)に向く、と説明しています。ビルドバックエンドは、グループの内容をパッケージのメタデータに入れてはいけない決まりです。
仕様は、依存のグループを、requirements.txt(pip 専用)でできることの一部を標準にしたもの、と考えるよう書いています。
書き方は、[dependency-groups] の下に、グループ名 = 依存の配列です(仕様の例)。
[dependency-groups]
docs = ["sphinx"]
test = ["pytest>7", "coverage"]
ほかのグループは、{include-group = "グループ名"} で取り込めます(例: test = ["pytest>7", {include-group = "coverage"}])。
extra(optional-dependencies)との違い
PEP 735 は、extra との違いを次のように説明しています。
- extra はパッケージのメタデータとして公開されます。グループは公開されません
- extra だけを入れることはできません。プロジェクト本体とその依存も、一緒に入ります
- グループの名前を extra と同じにするのは、避けるよう勧められています
uv の文書は、依存を書く場所を次のように分けています。
| 場所 | 中身 |
|---|---|
project.dependencies | 公開する依存 |
project.optional-dependencies | 公開する追加の依存(extra) |
dependency-groups | 開発用の、手元だけの依存 |
なお、uv の文書は、依存のグループは最近標準になったもので、全部の道具が対応しているとは限らない、と書いています。
手元の uv 0.13.0 が書いた pyproject.toml
uv init で作ったプロジェクトで、次の4つのコマンドを流しました(uv 0.13.0)。
uv add requests
uv add --dev pytest
uv add --group lint ruff
uv add --optional cli rich
そのあとの pyproject.toml は、次のとおりです。dependencies・[project.optional-dependencies]・[dependency-groups] に書き分けられ、版は >= の下限つきで書かれました。
[project]
name = "hello-uv"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"requests>=2.34.2",
]
[project.scripts]
hello-uv = "hello_uv:main"
[project.optional-dependencies]
cli = [
"rich>=15.0.0",
]
[build-system]
requires = ["uv_build>=0.13.0,<0.14.0"]
build-backend = "uv_build"
[dependency-groups]
dev = [
"pytest>=9.1.1",
]
lint = [
"ruff>=0.17.0",
]
dev グループは特別扱いで、uv run や uv sync で既定で入ります。--no-dev で外せます。ほかの名前のグループは、--group・--only-group・--no-group・--all-groups・--no-default-groups で、入れるかどうかを決めます。既定で入れるグループは、[tool.uv] の default-groups で変えられます。
その状態で uv sync を流すと、dev 以外のグループ(lint の ruff)と extra(cli の rich ほか)が外れました。
$ uv sync
Resolved 16 packages in 3ms
Uninstalled 4 packages in 9ms
- markdown-it-py==4.2.0
- mdurl==0.1.2
- rich==15.0.0
- ruff==0.17.0
uv sync --all-groups で ruff が入り、uv sync --no-dev で pytest たちが外れました。uv sync は既定で、extra を入れず、dev 以外のグループも入れません(sync のくわしくは Python の uv とは?の「ロックと同期」の節で説明しています)。
uv は、すべてのグループが互いに両立する前提で、まとめて解決してロックファイルを作ります。
古い記事では、[tool.uv] の dev-dependencies という書き方が出てきます。[dependency-groups] が標準になる前に uv が使っていたもので、いずれ非推奨になり、なくなる、と uv の文書は書いています。
pip でグループを入れる
pip は 25.1 から --group で、依存のグループを入れられます。Mac は python -m pip install --group groupA、Windows は py -m pip install --group groupA です(Windows は pip の文書の例で、手元では流していません)。
pip は pyproject.toml を探しに行きません。パスを書かなければ、今いるフォルダの pyproject.toml を読みます。別の場所なら、'./project/subproject/pyproject.toml:groupA' の形で書きます。
手元の pip 26.2.1 で pip install --group dev を流すと、pytest とその依存だけが入り、プロジェクト本体は入りませんでした。無いグループ名を渡すと、エラー(終了コード 1)になりました(途中の行は省いています)。
$ python -m pip install --group dev
Successfully installed iniconfig-2.3.1 packaging-26.3 pluggy-1.6.0 pygments-2.21.0 pytest-9.1.1
$ python -m pip install --group nosuch
ERROR: [dependency-groups] resolution failed for 'nosuch' from 'pyproject.toml': Dependency group 'nosuch' not found
グループは標準なので、道具をまたいで読めます。uv 0.13.0 が書いた pyproject.toml の lint グループを、pip 26.2.1 の pip install --group lint でそのまま入れられました(ruff 0.17.0 が入りました)。
pip の文書は、依存のグループには、requirements.txt と違って、pip のオプションなどパッケージ以外のものは書けない(標準の依存の書き方だけ)と注意しています。
グループの入れ方(コマンドの書き方)は、仕様では決まっておらず、道具ごとに用意されます。pip は --group、uv は --group・--dev などです。
ロックファイルとは?requirements.txt・uv.lock・pylock.toml
なぜロックファイルが要るのか
パッケージには依存があり、その依存にも依存があります。何が入ったかを書き残していないと、知らないうちに版が変わって、動かなくなることがあります。ロックファイルは、入れたものを書き残して、あとで同じものを入れられるようにするファイルです(PEP 751 の「教え方」の説明より)。みんなが同じロックファイルを使えば、同じパッケージが入ります。いつも同じファイルが入るので、悪意のあるファイルが紛れ込むのを防ぐ助けにもなります。
pyproject.toml は、プロジェクトの「大まかな要件」を書き、ロックファイルは、入れることになる「解決済みの正確な版」を書きます(uv の文書の言い方)。
JavaScript の package-lock.json と同じ考え方の話は、npm・yarn・pnpm の違いとは?どれを使う?(2026年版・サプライチェーン攻撃への備えまで)でも説明しています。
PEP 751 が出るまで、ロックファイルの標準はありませんでした。PDM・pip freeze・pip-tools・Poetry・uv が、それぞれのやり方で作っていました。
requirements.txt はロックファイルなのか
「requirements.txt はロックファイル」と一言では言えません。中身で分かれます。
- 版を
==で全部固定した requirements.txt: 「同じものを入れ直す」ために使われてきました。pip の文書は、pip freezeで作った、版を固定したファイルなら、依存の依存まで固定される、と説明しています。PEP 751 も、requirements.txt を「1つの用途のためのロックファイル」の例に挙げています - 名前だけの requirements.txt(例:
fastapi): 版が固定されていません。uv の移行ガイドは、人によって違う版が入りうる、と書いています
pip freeze 自体は、入っているものを報告するだけで、ロックファイルを計算するわけではありません。pip の文書には、同じものを入れ直すための段階も書かれています。まず == で版を固定します。--no-deps を付けて入れると、書いていないものが入らない保険になります。さらに、--hash=sha256:… のようにハッシュ(ファイルの中身から作る確認用の値)を付けると、取得したファイルを確かめられます。
PEP 751 は、標準に一番近かったのは pip の要件ファイル(requirements.txt)だが、標準ではなく慣習で支えられていて、既定では安全でもない(ハッシュの確認は任意など)、と見ています。
uv.lock
uv のプロジェクトで使うロックファイルは、uv.lock です。uv の文書は、次のように説明しています。
- OS・CPU・Python の版の違いをまたいで使える(universal)ロックファイルです
- 人が読める TOML ですが、uv が管理するので、手で直しません
- 形式は uv 専用で、ほかの道具は使えません
- Git に入れます。マシンが変わっても、同じ版で入れ直せます
requirements.txt と違って、1つのファイルで、依存のグループも、どの OS 向けもまとめて表せます。
pylock.toml:標準のロックファイル
pylock.toml は、PEP 751 で決まった、ロックファイルの標準の形式です。いまは PyPA の仕様になっています。Python の環境に同じものを入れ直すために、依存を書くファイルの形式です。
- 名前は
pylock.tomlです。名前を付けたいときや複数あるときは、pylock.<名前>.toml(例:pylock.spam.toml)にします - TOML で、人が読めますが、人が書くものではなく、道具が作るものです。入れるときに、依存の解決(どの版にするかの計算)をしなくてよいように作られています
- 自分の形式のロックファイルを持つ道具の「書き出し先」としても使えます
- 仕様に対応した道具なら、違う道具で作った pylock.toml でも入れられます。ただし、違う道具でロックして、同じ結果になるとは限りません
PEP 751 の日付は、資料によって表示が違います。PEP のページには「Resolution: 31-Mar-2025」とあり、PyPA の pylock.toml の仕様の履歴には「2025年4月: PEP 751 で承認」とあります。この記事では、日付はこの2つを並べるだけにします。
2026年10月10日時点では、pip と uv で pylock.toml を入れる側は、試験的の扱いです。 手元でも警告が出ました。
| 操作 | 道具 | 手元(pip 26.2.1・uv 0.13.0)の警告 |
|---|---|---|
pip lock(pip 25.1 から) | pip | pip lock は試験的なコマンドで、予告なく変わったり、なくなったりする |
pip install -r pylock.toml(pip 26.1 から) | pip | pylock.toml を要件の読み込み元にするのは試験的な機能 |
uv pip install -r pylock.toml | uv | --pylock のオプションは試験的 |
uv の文書は、pylock.toml を「書き出し先」と「uv pip のコマンド」で使える、と書き、試験的とは書いていません。一方、手元では uv pip install -r pylock.toml で警告が出ました(uv export -o pylock.toml の書き出しでは出ませんでした)。
pip で作る(pip lock)
pip lock は、いまの Python の版と OS でしか正しいと保証されないロックファイルを作ります。出力の名前の既定は pylock.toml です。pip の文書の例は、今のフォルダのプロジェクトのロックファイルを作る python -m pip lock -e .(Windows は py -m pip lock -e .)です。手元では -e を付けない python -m pip lock . を流しました(-e . の形は手元では流していません)。
$ python -m pip lock .
WARNING: pip lock is currently an experimental command. It may be removed/changed in a future release without prior warning.
(出力は一部を省いています)
できた pylock.toml の先頭は、次のとおりです(途中は省いています)。
lock-version = "1.0"
created-by = "pip"
[[packages]]
name = "certifi"
version = "2026.7.22"
この Mac で作ったファイルには、この Mac 用の wheel だけが書かれました(例: charset_normalizer は cp313-cp313-macosx_10_13_universal2 の1つ)。pip の文書の「いまの Python の版と OS でしか保証されない」と合っています。
手元の pip 26.2.1 では、pip lock . で作った pylock.toml にはプロジェクト自身(フォルダ)も入りました。それを pip install -r pylock.toml で入れようとすると、エラー(終了コード 1)になりました。
ERROR: Can't verify hashes for these file:// requirements because they point to directories:
(出力は一部を省いています)
この動きを説明した公式の文は、見つけられませんでした。手元の結果としてだけ書きます。依存だけをロックする pip lock --only-deps . -o pylock.deps.toml で作ったファイルは、pip install -r pylock.deps.toml で入りました(requests と依存4つ)。
uv で書き出す(uv export)
uv は、プロジェクトでは uv.lock を使い続けます。uv の機能の一部が pylock.toml の形で表せないからです。pylock.toml は、書き出し先と、uv pip のコマンドで使います。
# uv.lock を pylock.toml の形で書き出す
uv export -o pylock.toml
# requirements から pylock.toml を作る
uv pip compile requirements.in -o pylock.toml
# pylock.toml から入れる
uv pip sync pylock.toml
uv pip install -r pylock.toml
上の4つは uv の文書にあるコマンドです。手元で流したのは、uv export -o pylock.toml と uv pip install -r pylock.toml で、uv pip compile と uv pip sync は流していません。
uv export は、uv.lock を別の形式に書き出します。いまは requirements.txt・pylock.toml(PEP 751)・CycloneDX v1.5 JSON に対応し、出力ファイルの拡張子から形式を決めます(指定が無ければ requirements.txt)。--locked か --frozen が無いと、書き出す前にロックし直します。
手元の uv 0.13.0 の uv export -o pylock.toml は、コメント2行のあと、lock-version = "1.0"・created-by = "uv"・requires-python = ">=3.13" の行が続きました。既定で dev グループ(pytest)を含み、lint グループと extra は含みませんでした。Windows でだけ要る colorama には、条件(マーカー)が付きました。
name = "colorama"
version = "0.4.6"
marker = "sys_platform == 'win32'"
uv が書き出した pylock.toml を pip 26.2.1 の pip install -r で入れると、プロジェクト自身(editable のフォルダ)の行で、ハッシュを確かめられないというエラー(終了コード 1)になりました。uv export --no-emit-project -o pylock.toml(プロジェクト自身を出さない)で書き出すと、入りました。反対に、pip が作った pylock.toml(依存だけのもの)は、uv pip install -r でも入りました。
requirements.txt との違い(PEP 751 の説明)
| requirements.txt | uv.lock | pylock.toml | |
|---|---|---|---|
| 標準か | 標準ではなく、慣習で支えられている | uv 専用の形式 | PyPA の標準の仕様(PEP 751) |
| 読める道具 | pip のほか uv など。全部の書き方は pip が使うためのもの | uv だけ | 仕様に対応した道具 |
| 複数の用途 | 1つのファイルに1用途 | グループも OS もまとめて表せる | 1つのファイルで複数の用途(extra・グループ)を表せる |
PEP 751 は、対応する Python の版や環境をファイルの中にはっきり書く点も、この仕様の特徴と説明しています。
PEP 751 は、pylock.toml で requirements.txt を完全には置き換えないと書いています。requirements.txt には、入れるときのオプション(--index-url、--constraint など)、-r でほかのファイルを読むこと、環境変数が書けるのに、pylock.toml には書けないからです。pylock.toml では、ハッシュも必須です。
pip と uv での使い方の違い
仮想環境の考え方が違う
pip は、有効にした仮想環境(ほかから切り離された Python の環境)に入れる道具です。uv のプロジェクトは、有効にした環境を中心にせず、プロジェクトごとの .venv フォルダを自動で管理します。uv の文書は、環境の中でコマンドを動かすには uv run を使うのが勧められる、と書いています。uv の使い方の詳しいところは、Python の uv とは?使い方と pip・venv との違い【uv 0.12 対応】にまとめています。
pyproject.toml の依存を入れる
pip で、pyproject.toml の dependencies を入れるときは、pip install . を使います。プロジェクト本体も一緒に入ります。pip は、[build-system] のビルドバックエンドを先に入れてから、プロジェクトをビルドし、[project] の依存も入れます。
python -m pip install .
手元の pip 26.2.1 では、最後の行がこうなりました(途中の行は省いています)。requests とその依存4つ、プロジェクト本体の hello-pyproj が入っています。
Successfully installed certifi-2026.7.22 charset_normalizer-3.5.2 hello-pyproj-0.1.0 idna-3.20 requests-2.34.2 urllib3-2.8.0
本体を入れずに、依存だけを入れたいときは、pip 26.2 で足された --only-deps を使います。手元では pip install --only-deps . で、requests と依存4つだけが入りました。pip の文書の例はパッケージの名前を渡す形で、. を渡す例は文書にありません。--only-deps は、--group・-r・--no-deps とは一緒に使えません。
uv の pip 形式のコマンド uv pip install -r は、requirements.txt・pylock.toml・pyproject.toml・setup.py・setup.cfg などを読めます。pip の -r と違い、pyproject.toml も渡せます。手元の uv 0.13.0 では、uv pip install -r pyproject.toml で依存の5つが入り、プロジェクト本体は入りませんでした。--group dev を付けると、pytest たちも入りました。
この記事で流したコマンドの対応(Mac)
| やりたいこと | pip | uv |
|---|---|---|
| 依存を足す | pyproject.toml を手で直して python -m pip install . | uv add |
| 開発用のグループ | python -m pip install --group dev | uv add --dev(足す)、uv sync(入れる) |
| requirements.txt から入れる | python -m pip install -r requirements.txt | uv pip install -r requirements.txt |
| ロックファイルを作る | python -m pip lock .(試験的) | uv lock(uv.lock) |
| requirements.txt を書き出す | python -m pip freeze(入っているもの) | uv export(ロックファイルから) |
この表は、この記事で流したコマンドの組み合わせです。「対応する」と公式が書いているのは、uv の移行ガイドの「pyproject.toml は requirements.in の代わり、uv.lock は requirements.txt の代わり」だけです。
requirements.txt から pyproject.toml に移るには?
pip だけで requirements.txt を pyproject.toml に変換するコマンドは、この記事で開いた pip の文書には見つかりませんでした。変換のコマンドとして確かめたのは、uv の uv add -r です。
名前だけの requirements.in と、固定した requirements.txt に分ける
pip-tools では、依存を書くファイルを requirements.in、版を固定した結果を requirements.txt と、拡張子で分けます(uv の移行ガイドの説明)。名前だけの requirements.txt は、版が固定されていません。
まず uv init でプロジェクトを作り、uv add -r requirements.in で取り込みます。いまの固定した版を変えたくないときは、固定した requirements.txt を制約(-c)として渡します。
uv add -r requirements.in -c requirements.txt
手元(uv 0.13.0)では、requirements.in に requests の1行を手で書き、requirements.txt には pip freeze で出した requests まわりの5行(certifi・charset-normalizer・idna・requests・urllib3 を == で固定)を置きました。ファイルを1つ上のフォルダに置いたので、../ が付いています。結果の dependencies は次のとおりで、入った版は同じでした。
$ uv add -r ../requirements.in -c ../requirements.txt
$ sed -n "/^dependencies/,/^]/p" pyproject.toml
dependencies = [
"requests>=2.34.2",
]
(出力は一部を省いています)
pip freeze の結果をそのまま渡さない
同じ requirements.txt を、-c を使わずに uv add -r requirements.txt で取り込むと、依存の依存まで == で固定されて、dependencies に入りました。
$ uv add -r ../requirements.txt
$ sed -n "/^dependencies/,/^]/p" pyproject.toml
dependencies = [
"certifi==2026.7.22",
"charset-normalizer==3.5.2",
"idna==3.20",
"requests==2.34.2",
"urllib3==2.8.0",
]
(出力は一部を省いています)
PyPA は、依存を特定の版に固定したり、依存の依存まで書いたりするのは、良いやり方ではないと説明しています(install_requires についての説明で、dependencies はそれに当たります)。移すときは、uv の文書にある -r requirements.in -c requirements.txt の形にします。
開発用の要件ファイルは、uv の文書の例では、uv add --dev -r requirements-dev.in -c requirements-dev.txt で dev グループに、uv add -r requirements-docs.in -c requirements-docs.txt --group docs のように名前を付けて取り込みます(この2つは手元では流していません)。
逆に、uv から requirements.txt を作る
uv のプロジェクトから requirements.txt を作るときは、uv export --format requirements.txt です。手元では、既定でハッシュ(--hash=sha256:…)が付き、プロジェクト自身が -e . の行で入り、dev グループも含まれました。--no-dev --no-hashes を付けると、本番用の依存だけの短い形になりました。
$ uv export --format requirements.txt --no-dev --no-hashes
Resolved 16 packages in 5ms
# This file was autogenerated by uv via the following command:
# uv export --format requirements.txt --no-dev --no-hashes
-e .
certifi==2026.7.22
# via requests
charset-normalizer==3.5.2
# via requests
idna==3.20
# via requests
requests==2.34.2
# via hello-uv
urllib3==2.8.0
# via requests
よくある質問
requirements.txt はもう要らないのですか?
「もう要らない」とも「両方要る」とも言い切る公式の文は、この記事で開いた資料には見つかりませんでした。書かれているのは、次の3つです。
- PyPA は、
install_requires(pyproject.toml のdependenciesに当たる)は1つのプロジェクトの依存を、要件ファイルは Python の環境まるごとの要件を書くことが多い、と役割の違いを説明しています - uv の移行ガイドは、uv のプロジェクトでは、pyproject.toml が
requirements.inの代わり、uv.lockがrequirements.txtの代わりになると書いています - PEP 751 は、pylock.toml でも requirements.txt は完全には置き換わらない、と書いています(入れるときのオプションなどが書けないため)
pip で pyproject.toml から入れるには?
pip install -r pyproject.toml は、エラーになります(手元の pip 26.2.1)。プロジェクトの依存は、pip install . で入れます(プロジェクト本体も入ります)。本体を入れずに依存だけなら、pip 26.2 の --only-deps です。[dependency-groups] のグループは、pip 25.1 からの --group で入ります。uv なら、uv pip install -r pyproject.toml で読めます。
pyproject.toml から requirements.txt を作るには?
uv なら、uv export --format requirements.txt で、ロックファイル(uv.lock)から書き出せます。--no-dev --no-hashes を付けると、本番用の依存だけの短い形になります。
pip だけなら、先に pip install . で入れてから、pip freeze で書き出します。pip freeze は入っているものを出すだけで、ロックの計算ではありません。自分のプロジェクトが @ file://… の形で出るので、外すなら --exclude <名前> を付けます。
[build-system] は書かないといけませんか?
PyPA の手引きは、強く勧めています。仕様は、道具は [build-system] があることを求めるべきではない、と書いていて、無いときは仕様で決めた既定の値が使われます。「必須」とも「要らない」とも、一言では言えません。迷ったら、uv init やビルドバックエンドの文書が書く形を写します。
pylock.toml と uv.lock の違いは?
pylock.toml は、PEP 751 で決まった標準のロックファイルの形式で、仕様に対応した道具なら読めます。uv.lock は、uv 専用の形式で、ほかの道具は使えません。uv は、uv の機能の一部が pylock.toml で表せないので、プロジェクトでは uv.lock を使い続けます。uv export -o pylock.toml で、uv.lock から pylock.toml に書き出せます。pylock.toml を入れる側は、2026年10月10日時点で、pip・uv とも試験的の扱いです。
まとめ
- pyproject.toml は、Python のプロジェクトの名前・依存・ビルドの道具・各種の設定を書く設定ファイルです。仕様で決まった表は
[build-system]・[project]・[tool]で、[dependency-groups]は別の仕様(Dependency Groups)が決めた表です [project]では、固定で書くのはnameだけで、versionは固定かdynamicです。dependenciesは依存の一覧、optional-dependenciesは追加の機能(extra)、[project.scripts]はコマンドです[build-system]は、手引きは強く勧め、仕様は道具に存在を求めるべきではないとしています。uv_build の数字は uv の版で変わるので、uv initが書いた値を使います(uv 0.13.0 ではuv_build>=0.13.0,<0.14.0)- requirements.txt は、pip で入れるものの一覧です。pyproject.toml の依存は、pip がプロジェクトを入れるときに自動で読みます。requirements.txt は、
pip install -rで指定したときだけ使われます。pip install -r pyproject.tomlはできません - 版を全部固定した requirements.txt はロックファイルのように使われ、名前だけのものは固定されていません。標準のロックファイルの形式は pylock.toml ですが、2026年10月10日時点で、pip・uv とも入れる側は試験的です。uv のプロジェクトは uv.lock を使い続けます
[dependency-groups]は、開発の中だけで使う依存を書く場所で、パッケージのメタデータには入りません。pip は 25.1 から--groupで、uv はuv add --devなどで扱えます- requirements.txt から uv に移るときは、pip freeze の結果をそのまま
uv add -rに渡さず、-r requirements.in -c requirements.txtの形にします - 出力例は、pip 26.2.1・uv 0.13.0(macOS)のものです。版が変わると、文面や数字が変わることがあります
参考資料
この記事は、次の公式の資料をもとに書きました(2026年10月10日時点)。
- PyPA の手引き: Writing your pyproject.toml
- PyPA の仕様: pyproject.toml specification
- PyPA の仕様: Dependency Groups
- PyPA の仕様: pylock.toml Specification
- PyPA の仕様: Version specifiers
- PyPA: install_requires vs requirements files
- PyPA の用語集
- PEP 518
- PEP 621
- PEP 735
- PEP 751
- pip の文書: ユーザーガイド
- pip の文書: 要件ファイルの形式
- pip の文書: pip freeze
- pip の文書: pip lock
- pip の文書: pip install
- pip の文書: Repeatable Installs
- pip の文書: 変更履歴
- uv の公式: 依存の管理
- uv の公式: プロジェクトの構成
- uv の公式: pip からの移行
- uv の公式: コマンドのリファレンス
- uv の変更履歴(CHANGELOG)
- PyPI: pip
- PyPI: uv
※この記事は、公式の資料をもとに AI(Claude)で下書きし、2026年10月10日時点の公式の資料と照らして確かめてから公開しています。誤りに気づいたらお問い合わせからお知らせください。



