Pythonの開発環境はmise, Task, uv, Ruff, mypyで落ち着いた
はじめに
Pythonの開発環境をどう構築するかというのは皆さん悩んでいるのではないかと思います。
私もずっと悩んでいました。悩んでいたというより毎回試行錯誤しながら、それぞれのツールを試していました。
本当に多くの選択肢があり、どれを選べば良いのか難しい状態になっていると感じています。
私が使用してきた範囲だけでも下記のような選択肢がありました。
- Pythonのバージョン管理: pyenv、asdf、mise
- パッケージ管理: pyenv、poetry、rye、uv
- フォーマッター: black + isort、Ruff
- 型チェック: 使わない、mypy
- リンター: pylint、flake8、Ruff
- タスクランナー: Makefile, taskipy、poethepoet、Task
そんな中で、個人的にようやくこれだ!と思える組み合わせにたどり着くことができました。
この記事では、私が個人的に良いなって思っているPython開発環境を紹介させてください。
mise, Task, uv, Ruff, mypyという5つのツールを使用しています。
サンプルプロジェクトのディレクトリ構成
こういったPythonプロジェクトの作成を想定しています。
私はsrc-layoutを採用することが多いです。
my_package/
├── README.md
├── Taskfile.yml
├── mise.toml
├── pyproject.toml
├── src
│ └── my_package
│ ├── __init__.py
│ ├── moduleA
│ │ └── __init__.py
│ ├── moduleB
│ │ └── __init__.py
│ └── utils
│ └── __init__.py
├── tests
│ ├── __init__.py
│ └── test_my_package.py
└── uv.lock
このプロジェクトのサンプルはこちらに置いてあります。
mise
概要
miseは、Rustで開発されたプログラミング言語のバージョン管理ツールです。
miseを使うことで、それまで使っていたpyenvやnodenvなどの言語固有のツールから解放されスッキリしました。
好きなところ
- 複数言語のバージョン管理が1つのツールで完結できる
- asdfとの互換性があるおかげで、豊富なプラグインがそのまま使える
- プロジェクトごとの設定が
mise.toml
で簡単に管理できる
インストールと基本的な使い方
# Homebrewでのインストール
brew install mise
# activate
echo 'eval "$(/opt/homebrew/bin/mise activate zsh)"' >> ~/.zshrc
プロジェクトを作成していきます。
mkdir my_package
cd my_package
# Pythonの最新バージョンをインストール
mise use python@latest
生成されるmise.toml
の例を以下に示します。
[tools]
python = "latest"
インストールしたPythonのバージョンを確認してみます。
python -V # Python 3.13.1
Pythonのバージョンを固定する場合は以下のような書き方になります。
mise use [email protected]
miseでもタスクランナーの機能はありますが、私は今のところ使ってはいません。
miseのタスクランナーのファイル分割ができるのが良さそうなので、そのうちTaskから置き換えるかもしれません。
uv
概要
uvは、Pythonの高速なパッケージインストーラーおよび仮想環境マネージャーです。
uvを初めて使ったときはその速度が衝撃でした。
好きなところ
- パッケージのインストールが爆速!
- pipっぽい使い方もできる
- 既存の
requirements.txt
やpyproject.toml
からの移行の手間がまあまあ楽にできる
インストールと基本的な使い方
# miseを使ってuvをインストール
mise use uv@latest
# プロジェクトの初期化(再配布用のアプリケーションとして初期化)
uv init --app --package
# パッケージのインストール(例としてrequests, pandas, numpyをインストール)
uv add requests pandas numpy
# 開発用パッケージのインストール(例としてpytest, pytest-cov, pytest-mockをインストール)
uv add --dev pytest pytest-cov pytest-mock
ここまででできあがるpyproject.toml
の例を以下に示します。
[project]
name = "my-package"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
{ name = "author_name", email = "auther_emal" }
]
requires-python = ">=3.10"
dependencies = [
"numpy>=2.2.1",
"pandas>=2.2.3",
"requests>=2.32.3",
]
[project.scripts]
my-package = "my_package:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[dependency-groups]
dev = [
"pytest>=8.3.4",
"pytest-cov>=6.0.0",
"pytest-mock>=3.14.0",
]
Ruff
概要
Ruffは、Rustで書かれた高速なPythonのリンター兼フォーマッターです。
従来のblack、isort、flake8などの機能を1つのツールに統合しています。
好きなところ
- 爆速!
- --fixオプションで自動修正もできる
- デフォルトの設定でもいい感じに使える
インストールと基本設定
# 開発環境にRuffをインストール
uv add --dev ruff
pyproject.toml
での設定例を以下に示します。
プロジェクトに合わせて設定を変更してください。
[tool.ruff]
line-length = 120
[tool.ruff.format]
docstring-code-format = true
[tool.ruff.lint]
select = ["ALL"]
ignore = [
"T201",
"COM812",
"ISC001",
]
unfixable = [
"F401",
"F841",
]
pydocstyle.convention = "google"
[tool.ruff.per-file-ignores]
"tests/**/*.py" = [
"D",
"S101",
"N802",
"ARG",
]
[tool.ruff.lint.pylint]
max-args = 6
Task
概要
Taskは、Go言語で開発されたタスクランナーです。YAMLファイルでタスクを定義できます。
これまでは、各言語のパッケージ管理ツールやフレームワークにあるタスクランナーを使っていましたが、環境ごとに異なるタスクランナーを使い分けるのが面倒でした。
Taskは、それらのタスクランナーを1つに統合してくれるので、開発環境が変わってもタスクランナーを使い分ける必要がなくなりました。
好きなところ
- YAMLベースで読みやすい
- タスクの依存関係が分かりやすくメンテナンスが楽
- 各開発環境のパッケージ管理ツールやフレームワークに依存しない
- 使いやすいMakefileとして使える
インストールと基本設定
# miseを使ってTaskをインストール
mise use task@latest
Taskfile.yml
の設定例を以下に示します。
version: '3'
tasks:
default:
cmds:
- task --list
format:
desc: コードのフォーマット
cmds:
- uv run ruff format src tests
lint:
desc: コードの静的解析
cmds:
- uv run ruff check src tests
test:
desc: テストの実行
cmds:
- uv run pytest tests --cov=src --cov-report=term-missing --cov-branch
使用例
# 全タスクの一覧表示
task --list
# 特定のタスクの実行
task format
task lint
task test
mypy
概要
mypyは、Pythonの型チェックツールです。
Pythonで型チェックって本当に必要なの?と考える時期もありましたが、今では自分の中では必須ツールになっています。
特に大規模なプロジェクトの経験から、プロジェクトの初期段階からの導入が重要だと実感しています。
好きなところ
- 型チェックの厳密さが心強い
- エディタの補完が賢くなる
- リファクタリング時の安心感が増えた
- チーム開発での意思疎通が楽に。型定義を見れば使い方が分かる
- GitHub Copilotでコードの編集や補完の質が上がっている気がする
インストールと基本設定
# 開発環境にmypyをインストール
uv add --dev mypy
# 型スタブのインストール(例としてrequestsの型スタブをインストール)
uv add --dev types-requests
pyproject.toml
での設定例を以下に示します。
プロジェクトに合わせて設定を変更してください。
[tool.mypy]
# 基本設定
python_version = "3.12"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
# サードパーティライブラリの型チェック
ignore_missing_imports = true
# 特定のモジュールの設定
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
型チェックの実行例を以下に示します。
# 基本的な型チェック
uv run mypy src
# 詳細な出力
uv run mypy src --verbose
# 特定のファイルのみチェック
uv run mypy src/mypackage/module.py
さらに堅牢性を上げるために
ここからは私の「推しツール」的なおまけです。
基本セットに加えて、プロジェクトやチームの特性に合わせてこれらのツールを導入すると更に開発体験が良くなります。
pre-commit
コミット前のチェックを自動化してくれるツールです。
「あ、コミット前にチェックするの忘れてた...」という失敗が完全になくなります。
開発チームでエディタやエディタの設定が統一できる場合は、導入しなくても良いかもしれません。
# miseを使ってpre-commitをインストール
mise use pre-commit@latest
.pre-commit-config.yaml
の設定例を以下に示します。
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.8.4
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.14.0
hooks:
- id: mypy
初期化例を以下に示します。gitの初期化が完了していることが前提です。
pre-commit install
これで、git commit時に自動でコードチェックが走るようになります。
最初は「面倒くさいなぁ」と思うこともあるかもしれませんが、特にチーム開発では安心感が上がります。
import-linter(依存関係の整理に悩んでいる人におすすめ!)
import-linterは、Pythonのパッケージ間の依存関係をチェックするツールです。
# import-linterをインストール
uv add --dev import-linter
.importlinter
の設定例を以下に示します。
[importlinter]
root_package = my_package
[importlinter:contract:1]
name = utilsから他パッケージへの参照を禁止
type = forbidden
source_modules = my_package.utils
forbidden_modules = [
my_package.moduleA,
my_package.moduleB,
]
[importlinter:contract:2]
name = moduleAからmoduleBへの依存を禁止
type = forbidden
source_modules = my_package.moduleA
forbidden_modules = my_package.moduleB
チェックの実行例を以下に示します。
uv run lint-imports
このツールの良いところは、アーキテクチャの制約を自動的にチェックできることです。
「このモジュールはこのモジュールに依存してはいけない」というルールを強制できます。
チーム開発では特に重要で、知らず知らずのうちにアーキテクチャが崩れていくのを防いでくれます。
おわりに
これらのツールを使うことで、ずっと悩んでいたPythonの開発環境をどうする問題が解決して落ち着いた気がしています。
この記事が、あなたのPython開発環境構築の参考になれば嬉しいです。
Discussion