Error [module 'pkgutil' has no attribute 'ImpImporter']

AttributeError: module 'pkgutil' has no attribute 'ImpImporter' の直し方(Python 3.12 と古い setuptools の食い違い)

FIX SUMMARY verified
Applies when
python:3.12-slimsetuptools <66.1 (pkg_resources references removed pkgutil.ImpImporter)importing pkg_resources on Python 3.12 with old setuptools (venv seed / pinned setuptools / old pip); fix window is setuptools 66.1..81

Verified: reproduced in python:3.12-slim, then the module 'pkgutil' has no attribute 'ImpImporter' signature was gone after the fix (exit 0).

Python 3.12 に上げてから、import pkg_resources(あるいは pkg_resources を使うツールの起動)で次のエラーが出て止まることがあります。

AttributeError: module 'pkgutil' has no attribute 'ImpImporter'. Did you mean: 'zipimporter'?

トレースバックをたどると、pkg_resources/__init__.py の中で pkgutil.ImpImporter を参照しているところで落ちています。これは、Python 3.12 が pkgutil.ImpImporter を削除したのに、入っている setuptools(の pkg_resources)が古くてそれをまだ使っているために起きます。

なぜ 3.12 で落ちるのか

pkgutil.ImpImporter は古い import 機構(imp モジュール)に連なる仕組みで、Python 3.3 で非推奨になり、3.12 で削除されました。代わりが用意されたわけではありません。3.12 では ImpImporterImpLoader も、その土台だった imp モジュール自体も無くなっています。一方、古い setuptools に同梱される pkg_resources は、読み込まれるときに pkgutil.ImpImporter を登録しようとします。3.11 まではその属性があったので通っていましたが、3.12 では無くなったため、import pkg_resources の途中で AttributeError になります。

pkg_resources は setuptools が提供するモジュールなので、直すべきは Python でも自分のコードでもなく、setuptools の版です。

なぜ Python 3.12 で出るのか

同じ古い setuptools でも、Python 3.11 までなら通りますpkgutil.ImpImporter がまだあるため)。エラーになるのは Python を 3.12 に上げた後です。手元やこれまでの環境が 3.11 以前なら踏みません。3.12 に上げたのに setuptools が古いまま、という組み合わせで表面化します。setuptools が古いまま残るのは、次のような場面です。

  • どこかで明示的に古い setuptools が入っている:Python 3.12 の標準 venv(python -m venv)は、ensurepip の変更で既定では setuptools を seed しません。ですから素の 3.12 venv でこの症状が出るなら、依存でのピン、以前の環境からの持ち越し、ビルド隔離の requires、サードパーティ virtualenv の seed キャッシュなど、どこかで古い setuptools を明示的に入れている、と切り分けられます。
  • requirements.txt や制約ファイルで setuptools を固定している:再現性のために古い版をピンしていると、3.12 でだけ落ちます。
  • 古い pip / CI イメージ / tox:pip が古いと seed される setuptools も古くなります。

直し方1:setuptools を 3.12 対応版へ上げる(ただし窓に注意)

基本は setuptools を新しくすることです。3.12 対応版(66.1 以降)なら pkg_resourcespkgutil.ImpImporter を参照しなくなっています。

pip install --upgrade "setuptools>=66.1"

ただし、上げすぎにも境界があります。setuptools は後の版(67.5 以降で非推奨、82 で削除)で pkg_resources 自体を同梱しなくなりました。pkg_resources を使うコードやツールを動かしたいのに最新へ上げると、今度は No module named 'pkg_resources' に変わります。import pkg_resources を通したいなら、3.12 に対応しつつ pkg_resources をまだ含む版(おおよそ 66.1〜81 の窓)に収めます。

pip install "setuptools>=66.1,<82"

pip install --upgrade setuptools だけで最新へ跳ぶと、エラーが ImpImporter から pkg_resources 不在へすり替わることがあります。今 pkg_resources が要るのかどうかで、上げ先を決めてください。

直し方2:pkg_resources から離れる/古い供給元を直す(恒久)

pkg_resources は非推奨で、将来の setuptools では外れていきます。版の窓で凌ぐのは当座の措置なので、根の方も見ておきます。

  • 自分のコードが pkg_resources を使っているなら移行する:バージョン取得は importlib.metadata、同梱データの読み出しは importlib.resources に置き換えられます(どちらも標準ライブラリ)。移行すれば版の窓を気にせずに済みます。
  • 古い setuptools がどこから来るかを断つ:仮想環境を作り直す、python -m venv を新しい Python で張り直す、pip install --upgrade pip setuptools を環境構築の最初に入れる、といった形で seed を新しくします。
  • 落ちているのが第三者ツールの pkg_resources import なら、そのツールを新しくする:ツール側が pkg_resources 依存をやめている新版があることが多いです。

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

  • setuptools を上げたら、今度は No module named 'pkg_resources' になった:上げすぎです。pkg_resources を含む版(66.1〜81 の窓)へ下げます。詳細は No module named ‘pkg_resources’ を参照してください。
  • No module named 'distutils' が出ている:これは別で、3.12 が distutils を削除したことによるものです。対処は No module named ‘distutils’ を参照してください(同じ 3.12 の標準ライブラリ削除の一族です)。
  • setuptools を上げたのに、まだ古い版が使われる:仮想環境の外側(システム側)を直していないか、環境を有効化していない可能性があります。python -c "import setuptools; print(setuptools.__version__)" で実際に使われている版を確かめます。
  • pip install の途中(ビルド段)で出ている:PEP 517 のビルド隔離では、pip が別環境を作って pyproject の [build-system].requires に従い setuptools を入れ直すため、外側の setuptools を上げても直らないことがあります。対象プロジェクトの requires や lock を対応版にするか、対応版を固定してください(トレースバックに pip-build-env が出るのが目印です)。

検証環境

  • python:3.12-slim、ネットワーク有り
  • 再現:pip install "setuptools==65.5.1" の後に import pkg_resources すると、module 'pkgutil' has no attribute 'ImpImporter' を出して終了コード 1
  • 修正:pip install "setuptools==75.6.0" の後に import pkg_resources すると、終了コード 0

再現から修正までは errfix の検証ハーネスが機械的に確認しています。境界が Python 3.12 であることは、対照として python:3.11-slim(同じ setuptools==65.5.1)で import pkg_resources が通ることを確かめて裏づけました。上げすぎると pkg_resources 不在に変わる点も実機で確認しています(機械検証の fix は pkg_resources を含む 75.6.0)。エラー文言は実機出力から引用しています。virtualenv の seed や CI の供給元については、機械検証の対象は上記に限り、一般的な引き金として記述しています(pkgutil.ImpImporter の削除は CPython 3.12 のリリースノートに拠ります)。

検証(machine-verified)

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

verify — run-case.mjs
$ node run-case.mjs python/pkgutil-impimporter-python-312
● reproduce module 'pkgutil' has no attribute 'ImpImporter' present ✓
● apply fix exit 0
● re-run module 'pkgutil' has no attribute 'ImpImporter' gone ✓
PASS verified · python:3.12-slim · signature gone