pyproject.toml とは?requirements.txt との違い【pip 26.2・uv 0.13】

PR
pyproject.toml とは?requirements.txt との違い【pip 26.2・uv 0.13】
この記事は約40分で読めます。

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 で流したものです(仮想環境の場所は省いています)。長い出力は、途中を省いたものがあります
  1. pyproject.toml とは?何を書くファイルか
    1. pyproject.toml は設定ファイル
    2. 表は何があるか
    3. なぜ生まれたのか
  2. pyproject.toml の書き方(build-system・project・tool)
    1. [build-system]:ビルドの道具を決める
    2. [project]:名前・版・依存を書く
      1. name と version
      2. dependencies:依存の一覧
      3. optional-dependencies:特定の機能のときだけ要る依存
      4. requires-python と scripts
      5. license
    3. [tool]:道具ごとの設定
  3. pyproject.toml と requirements.txt の違いは?
    1. requirements.txt とは
    2. 違いを表で見る
    3. 手元で比べる
    4. pip install -r pyproject.toml はできない
  4. 依存のグループ([dependency-groups])とは?extra との違い
    1. 開発の中だけで使う依存を入れる場所
    2. extra(optional-dependencies)との違い
    3. 手元の uv 0.13.0 が書いた pyproject.toml
    4. pip でグループを入れる
  5. ロックファイルとは?requirements.txt・uv.lock・pylock.toml
    1. なぜロックファイルが要るのか
    2. requirements.txt はロックファイルなのか
    3. uv.lock
    4. pylock.toml:標準のロックファイル
      1. pip で作る(pip lock)
      2. uv で書き出す(uv export)
    5. requirements.txt との違い(PEP 751 の説明)
  6. pip と uv での使い方の違い
    1. 仮想環境の考え方が違う
    2. pyproject.toml の依存を入れる
    3. この記事で流したコマンドの対応(Mac)
  7. requirements.txt から pyproject.toml に移るには?
    1. 名前だけの requirements.in と、固定した requirements.txt に分ける
    2. pip freeze の結果をそのまま渡さない
    3. 逆に、uv から requirements.txt を作る
  8. よくある質問
    1. requirements.txt はもう要らないのですか?
    2. pip で pyproject.toml から入れるには?
    3. pyproject.toml から requirements.txt を作るには?
    4. [build-system] は書かないといけませんか?
    5. pylock.toml と uv.lock の違いは?
  9. まとめ
  10. 参考資料

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 の手引きにある例は、次のとおりです。

ビルドバックエンドrequiresbuild-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.tomlrequirements.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 から)pippip lock は試験的なコマンドで、予告なく変わったり、なくなったりする
pip install -r pylock.toml(pip 26.1 から)pippylock.toml を要件の読み込み元にするのは試験的な機能
uv pip install -r pylock.tomluv--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.txtuv.lockpylock.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)

やりたいことpipuv
依存を足すpyproject.toml を手で直して python -m pip install .uv add
開発用のグループpython -m pip install --group devuv add --dev(足す)、uv sync(入れる)
requirements.txt から入れるpython -m pip install -r requirements.txtuv 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日時点)。

※この記事は、公式の資料をもとに AI(Claude)で下書きし、2026年10月10日時点の公式の資料と照らして確かめてから公開しています。誤りに気づいたらお問い合わせからお知らせください。

PR
PR
タイトルとURLをコピーしました