🐍

Pythonの開発環境はmise, Task, uv, Ruff, mypyで落ち着いた

2025/01/01に公開

はじめに

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

このプロジェクトのサンプルはこちらに置いてあります。
https://github.com/mmocchi/my_package

mise

概要

miseは、Rustで開発されたプログラミング言語のバージョン管理ツールです。
miseを使うことで、それまで使っていたpyenvやnodenvなどの言語固有のツールから解放されスッキリしました。

mise公式サイト

好きなところ

  • 複数言語のバージョン管理が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でもタスクランナーの機能はありますが、私は今のところ使ってはいません。
miseのタスクランナーのファイル分割ができるのが良さそうなので、そのうちTaskから置き換えるかもしれません。

uv

概要

uvは、Pythonの高速なパッケージインストーラーおよび仮想環境マネージャーです。
uvを初めて使ったときはその速度が衝撃でした。

uv公式リポジトリ

好きなところ

  • パッケージのインストールが爆速!
  • pipっぽい使い方もできる
  • 既存のrequirements.txtpyproject.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つのツールに統合しています。

Ruff公式ドキュメント

好きなところ

  • 爆速!
  • --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つに統合してくれるので、開発環境が変わってもタスクランナーを使い分ける必要がなくなりました。

Task公式サイト

好きなところ

  • 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で型チェックって本当に必要なの?と考える時期もありましたが、今では自分の中では必須ツールになっています。
特に大規模なプロジェクトの経験から、プロジェクトの初期段階からの導入が重要だと実感しています。

mypy公式サイト

好きなところ

  • 型チェックの厳密さが心強い
  • エディタの補完が賢くなる
  • リファクタリング時の安心感が増えた
  • チーム開発での意思疎通が楽に。型定義を見れば使い方が分かる
  • 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

コミット前のチェックを自動化してくれるツールです。
「あ、コミット前にチェックするの忘れてた...」という失敗が完全になくなります。
開発チームでエディタやエディタの設定が統一できる場合は、導入しなくても良いかもしれません。

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公式サイト

# 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