[初心者向け] uv入門!インストール方法から使い方、コマンド、パッケージの管理方法まで徹底解説

## uvとは
Rust製のPythonのパッケージマネージャーで既存のパッケージマネージャーで有名なPoetryやpipと比べてパッケージのインストールや仮想環境の作成がかなり高速なことで知られています

## インストール方法
以下のコマンドを実行するだけでインストールが完了します

```
curl -LsSf https://astral.sh/uv/install.sh | sh
```

## 初期設定
uv initコマンドで新規プロジェクトを作成できます

```
uv init application
Initialized project `application` at `/Users/shun198/Desktop/uv-practice/application`
```

以下のようにプロジェクトフォルダ内にファイル群が自動生成されます
```
tree
.
├── README.md
└── application
    ├── README.md
    ├── hello.py
    └── pyproject.toml
```

## パッケージの追加
パッケージの追加はuv addコマンドで行います

```
uv add flask
Using CPython 3.11.3 interpreter at: /Users/shun198/.pyenv/versions/3.11.3/bin/python3.11
Creating virtual environment at: .venv
Resolved 9 packages in 217ms
Prepared 7 packages in 123ms
Installed 7 packages in 57ms
 + blinker==1.9.0
 + click==8.1.7
 + flask==3.1.0
 + itsdangerous==2.2.0
 + jinja2==3.1.4
 + markupsafe==3.0.2
 + werkzeug==3.1.3
```

パッケージが追加されるとプロジェクトフォルダ内に
- .venv
- .python-version
- uv.lock

が自動生成されます

```
tree
.
├── README.md
└── application
    ├── .venv
    ├── .python-version
    ├── README.md
    ├── hello.py
    ├── pyproject.toml
    └── uv.lock
```

pyproject.tomlのdependenciesにパッケージが追加されているのを確認できます

```pyproject.toml
[project]
name = "application"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "flask>=3.1.0",
]
```

開発環境でのみ使用したパッケージがある場合は--devのオプションを付与することでdev用のグループにパッケージを追加できます

```
uv add ruff --dev
Resolved 10 packages in 26ms
Installed 1 package in 19ms
 + ruff==0.9.5
```

```pyproject.toml
[project]
name = "application"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "flask>=3.1.0",
]

[dependency-groups]
dev = [
    "ruff>=0.9.5",
]
```

また、Poetry以下のように各パッケージのオプションを追記することもできます
下記はruffのオプションです

```
[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
    ".bzr",
    ".direnv",
    ".eggs",
    ".git",
    ".git-rewrite",
    ".hg",
    ".mypy_cache",
    ".nox",
    ".pants.d",
    ".pytype",
    ".ruff_cache",
    ".svn",
    ".tox",
    ".venv",
    "__pypackages__",
    "_build",
    "buck-out",
    "build",
    "dist",
    "node_modules",
    "venv",
]

# Same as Black.
indent-width = 4
line-length = 88

# Assume Python 3.12
target-version = "py312"

[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`)  codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
ignore = ["D100", "D101", "D102", "D103", "D104", "D107", "D202", "D203", "D205", "D212", "D213", "D400", "D406", "D407", "D413", "D415"]
select = ["E4", "E7", "E9", "F", "D"]

# Allow fix for all enabled rules (when `--fix`) is provided.
fixable = ["ALL"]
unfixable = []

# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"

# Like Black, indent with spaces, rather than tabs.
indent-style = "space"

# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false

# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
```

## パッケージの削除
uv removeコマンドでパッケージを削除できます

```
uv remove ruff
Resolved 9 packages in 67ms
Uninstalled 1 package in 4ms
 - ruff==0.9.5
```

## パッケージのインストール
### pyproject.toml内のパッケージを全てインストールする時
uv syncコマンドを実行します

```
uv sync
Resolved 10 packages in 16ms
Prepared 8 packages in 4.32s
Uninstalled 8 packages in 67ms
Installed 8 packages in 42ms
 ~ blinker==1.9.0
 ~ click==8.1.7
 ~ flask==3.1.0
 ~ itsdangerous==2.2.0
 ~ jinja2==3.1.4
 ~ markupsafe==3.0.2
 ~ ruff==0.9.5
 ~ werkzeug==3.1.3
```

### 特定のグループを除外するとき
devのグループ内のパッケージ以外をインストールする場合は--no-devのオプションを付与することで実現できます

```
uv sync --no-dev
```

### 依存関係を解決させずにインストールしたいとき
uv syncコマンドだと依存関係を自動で解決してしまうため、CI/CDなど依存関係にあるパッケージが競合した際にインストールを中断させることができます
--frozenオプションを付与することで実現できます

```
uv sync --frozen
```

## コマンドの実行
今回はFlaskの簡易的なプロジェクトを例に説明します

```
tree
.
├── README.md
└── application
    ├── .venv
    ├── .python-version
    ├── README.md
    ├── main.py
    ├── pyproject.toml
    └── uv.lock
```

```main.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return {"msg":"Hello!"}
```

Poetryみたいにuv runの後に実行したいコマンドを記載するとuv経由で実行できます

```
uv run flask --app main run -h 0.0.0.0 -p 8000
 * Serving Flask app 'main'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8000
 * Running on http://192.168.10.105:8000
Press CTRL+C to quit
```

![スクリーンショット 2025-02-08 8.28.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625980/fd11d1cb-f4e1-e6ab-d2b3-21d74a939aad.png)

## Docker環境で実行したい場合は
uvをDocker環境で実行する方法について解説します
今回はFlask用のDockerfileと実行用のentrypoint.shを例に説明します

```
tree
.
├── .env
├── .gitignore
├── Makefile
├── README.md
├── application
│   ├── .python-version
│   ├── .venv
│   ├── README.md
│   ├── main.py
│   ├── pyproject.toml
│   └── uv.lock
├── containers
│   ├── flask
│   │   ├── Dockerfile
│   │   └── entrypoint.sh
│   └── postgres
│       └── Dockerfile
└── docker-compose.yml
```

今回はastralが用意した、uvが事前にインストールされたDockerfileを使用します

https://docs.astral.sh/uv/guides/integration/docker/#available-images

uv syncコマンドでuvが管理しているパッケージをインストールできます

```Dockerfile
# https://docs.astral.sh/uv/guides/integration/docker/#available-images
FROM ghcr.io/astral-sh/uv:python3.11-alpine
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY application/pyproject.toml /code/
RUN uv sync
COPY ./containers/flask/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
```

uv runコマンドでFlaskを実行できます
```entrypoint.sh
#!/bin/sh
set -eu

uv run flask --app main run --debug -h 0.0.0.0 -p 8000
```

Flaskを使ったDocker環境の構築方法について詳細に知りたい方は以下の記事を参考にしてください

https://qiita.com/shun198/items/8c76a92a75c2adc16ca5

## CI/CD環境で実行したい時
GitHubが公式で出しているsetup-pythonとastralのsetup-uvを使ってワークフローを作成するのが一般的です
下記を記述することでPythonとuvの環境を用意することができます

```yaml
steps:
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version-file: .python-version
    - name: Setup uv
      uses: astral-sh/setup-uv@v5
      with:
        enable-cache: true
        cache-dependency-glob: uv.lock
```

## まとめ
uvを使うとパッケージのインストールがかなり早くなって開発体験が向上することを感じました
今後uvを使用した際に得た新たな知見を本記事に定期的に書いていきたいと思います

## 参考
https://docs.astral.sh/uv/

https://github.com/astral-sh/uv

https://docs.astral.sh/uv/concepts/projects/dependencies/

https://github.com/astral-sh/setup-uv