Error [No module named 'distutils']

ModuleNotFoundError: No module named 'distutils' の直し方(Python 3.12 で distutils が消えた)

FIX SUMMARY verified
Applies when
python:3.12-slimPython stdlib (distutils removed in 3.12 / PEP 632)import distutils on Python 3.12+ without setuptools installed

Verified: reproduced in python:3.12-slim, then the No module named 'distutils' signature was gone after the fix (exit 0).

Python 3.11 までは動いていたスクリプトやツールが、3.12 に上げた環境で次のエラーで止まることがあります。

ModuleNotFoundError: No module named 'distutils'

コードは何も変えていないのに、import distutils(や from distutils.util import strtobool のような読み込み)が急に見つからなくなります。原因は、distutils が Python 3.12 で標準ライブラリから削除されたことです。あなたのコードではなく、Python 側から機能が1つ無くなったために出ています。

急いで直すなら、pip install setuptools で戻せます。ただし「なぜ消えたのか」「なぜ setuptools で戻るのか」を押さえておくと、同じ環境で繰り返さずに済みます。

なぜ手元(3.11 以前)では出ないのか

distutils はパッケージのビルドや設定に使われてきた古い標準ライブラリで、Python 3.12 で削除されましたPEP 632 で予告されていた撤去です)。ここが境界です。

  • Python 3.11 以前distutils は標準ライブラリに含まれていました。だから import distutils は、追加のインストールなしにそのまま通っていました。
  • Python 3.12 以降 … 標準ライブラリから distutils が無くなりました。import distutils が通るのは、setuptools がインストールされていて、その複製を提供しているときだけです。

「3.11 では通っていた」のは、標準ライブラリに distutils があったからです。3.12 でそれが消え、いまや distutils の供給源は setuptools に移りました。開発機や以前の CI(3.11)では出ないのに、3.12 に上げたクリーンな環境(新しい venv、python:3.12-slim などの最小イメージ、CI)でだけ No module named 'distutils' が出る、というすれ違いになります。落ちている環境の python --version と、setuptools が入っているか(pip show setuptools)を先に確認してください。

直し方:setuptools を入れる

setuptools は、自前に distutils の複製を持っていて、import distutils をその複製へ振り向ける仕組み(_distutils_hack)を備えています。3.12 以降で distutils が要るなら、これを入れます。

pip install setuptools

これで import distutilsfrom distutils.util import strtobool も通るようになります。setuptools は活発に更新されているパッケージですが、新しい版(この記事の確認時点の最新でも)でも distutils の複製は提供されているので、版を古く固定する必要はありません。

恒久的には、distutils の import 自体をやめるのが本筋です。distutils は削除済みで、今後も標準ライブラリには戻りません。いま setuptools 経由で動いているのは、あくまで互換のための複製です。自分のコードなら、用途に応じて置き換えられます。

  • バージョン比較の distutils.version.LooseVersionpackagingpackaging.version.Version。ただし Version は PEP 440 準拠で、非準拠の版文字列を渡すと InvalidVersion を投げます。緩いパースに頼っていた箇所は挙動差に注意してください。
  • distutils.util.strtobool → 真偽の変換を自前の小さな関数に置き換える。元の strtobool は大文字小文字を無視して yes/true/on/1 などを真、no/false/off/0 などを偽とし、それ以外は ValueError を投げ、戻り値は 10(int)です。同じ判定が要るなら、この契約(真の集合・偽の集合・不正値は例外・戻り値は 1/0)を踏襲してください。
  • distutils.core.setup によるパッケージング → setuptools の API(from setuptools import setup)や、pyproject.toml ベースのビルドへ。

依存しているのが自分では直せない第三者パッケージの場合は、そのパッケージが 3.12 に対応した新しい版を出していないかを先に確認します。無ければ、当面は setuptools を入れて動かしつつ、対応版が出たら乗り換えるのが現実的です。

版・環境の境界

errfix の検証ハーネスで実測した点です。同じスクリプト(from distutils.util import strtobool)を、環境だけ入れ替えて動かしました。

環境import distutils
Python 3.11(標準ライブラリに distutils あり)通る
Python 3.12 / 3.13(setuptools 無し)No module named 'distutils'
Python 3.12 / 3.13(pip install setuptools 後)通る(setuptools の複製が提供)

(削除そのものは PEP 632 に基づきます。setuptools が複製を提供する挙動は、確認した版の範囲での実測です。「あらゆる setuptools 版で提供される」とまでは言えないので、動かない場合は下の切り分けを見てください。なお python:3.11-slim のように setuptools が同梱された環境では、3.11 でも import distutils は setuptools の複製経由で解決されます。stdlib と複製のどちらでも通るため、表の「通る」は変わりません。)

切り分け(うまくいかないとき)

  • No module named 'pkg_resources' で来た(似た見出しの別エラー)pkg_resourcessetuptools 由来のモジュールですが、直し方は逆で、pip install setuptools を新しく打っても戻りません(setuptools 82.0.0 で削除されたためです)。No module named ‘pkg_resources’ の直し方 を参照してください。
  • pip install setuptools しても直らない:その pip が、落ちている python と同じ環境を指しているかを確認してください。仮想環境が有効になっていない、あるいは複数の Python が混在していると、別の環境に setuptools を入れていることがあります。python -m pip install setuptools のように、落ちている python から pip を呼ぶと取り違えを避けられます。
  • pip install の途中で別のパッケージが No module named 'distutils' で失敗する:第三者パッケージの setup.py(ビルドスクリプト)が distutils を読み込んでいて、3.12 のビルド環境にそれが無い、という形です。多くはそのパッケージを 3.12 対応の新しい版に上げると解決します。上げられない事情があるなら、ビルド環境に setuptools を用意します。
  • No module named 'distutils.msvccompiler' のように、サブモジュール名まで付いているdistutils 全体ではなく、その中の一部を読み込もうとしています。setuptools の複製に存在しないサブモジュールだと、setuptools を入れても解決しません。読み込んでいる側のコードが、削除された(あるいは複製に含まれない)内部モジュールに依存していないかを確認してください。
  • ImportError: cannot import name '...' from 'distutils...'No module named ではない)distutils 自体は見つかっているが、その中の特定の名前が無いケースです。本記事の No module named(モジュールごと無い)とは別症状で、読み込もうとしている関数・クラスが(setuptools の複製の版差などで)存在しないことが多く、別の切り分けになります。実際に読み込まれている distutils.__file__setuptools の版を確認してください。
  • 警告として DeprecationWarning: distutils Version classes are deprecated が出るだけで動いている:まだ落ちてはいませんが、distutils に依存している合図です。3.12 に上げる前に、上記の恒久策(packaging などへの置き換え)を進めておくと、移行時にこのエラーを踏みません。

検証環境

  • python:3.12-slim(再現)/pip install setuptools 後(修正)。python:3.11-slim では同じスクリプトが通ることも確認
  • 再現:from distutils.util import strtoboolModuleNotFoundError: No module named 'distutils' で終了コード 1
  • 修正:pip install setuptools の後、同じスクリプトが strtobool('yes') を正常に評価して終了コード 0

再現から修正までは errfix の検証ハーネスが機械的に確認しています。3.12 のクリーンな環境で distutils の読み込みが落ちること、setuptools を入れると同じコードが通ることを、再現→修正→シグネチャ消滅として通しています。3.12 で distutils が標準ライブラリから外れたこと自体は PEP 632 に拠ります。

検証(machine-verified)

この修正は python:3.12-slim のバージョン固定コンテナ内で再現し、修正後に No module named 'distutils' のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs python/distutils-not-found-python-312
● reproduce No module named 'distutils' present ✓
● apply fix exit 0
● re-run No module named 'distutils' gone ✓
PASS verified · python:3.12-slim · signature gone