condaとpip:混ぜるな危険

Anaconda環境下でpipを使う場合のリスクについて、日本語で書かれたページがほとんど見つからなかったので覚え書き。

ひとことで

Anaconda下でpipを使うと予期せず環境が破壊され、最悪の場合Anaconda自体の再インストールが必要になる。pipは慎重に使いましょう。

condaとpip

pipPython環境で様々なパッケージを管理するための標準ツール。
例えば、pip install numpyというコマンド一発で(依存関係も含め)PyPI(配布サイト)からnumpyをダウンロード・インストールすることができる。

一方、特にデータサイエンスのためにPythonを使う人に人気なのが、Anaconda, Inc.社の提供するAnacondaというPythonディストリビューション
www.anaconda.com
Anacondaにはcondaという独自のパッケージマネージャーが付属しており、仮想環境の管理やpipの代わりの役割などを果たしている。
例えば、conda install numpyからnumpyをインストールできる。

なぜAnaconda?

筆者がPythonを使い始めた頃 (2013~2014年ぐらい)はPythonの公式側でパッケージ管理の仕組みがまだ整備途中の段階だった。特にWindowsではnumpyの様な(他言語で書かれたライブラリのコンパイルを伴う)パッケージをpipから入れるのは難しかった。そのため、非公式サイトからビルド済のファイルを逐一ダウンロードする必要があるなど不便が多かった。

Anacondaではその辺の不便が解消されており、当時から一発で必要なパッケージをすべてインストールできた(今となっては大半のパッケージがpipで入れられるように整備されている)。

condaの仕組み

condaとpipとでインストール等のコマンドはよく似ているのだが、両者のパッケージの仕組みは全く異なり、基本的に互換性が一切ない。また、以下の引用文から分かるように、condaは複数の仮想環境下でディスクの使用量を節約するため、ハードリンクを駆使している。便利な機能なものの、これが後々問題となることがある。

Conda installs packages into environments efficiently using hard links by default when it is possible, so environments are space efficient, and take seconds to create.

https://conda.readthedocs.io/en/latest/

pipとcondaの衝突

Anaconda下では基本的にcondaを使ってパッケージをインストールするのだが、一部のパッケージはAnaconda社のレポジトリからは提供されていない。そのような場合にとるべきアプローチはいくつかある。

  1. デフォルト以外のレポジトリ(チャネル)からインストール(例: conda install -c matsci pymatgen
  2. 自分でconda用のパッケージを作る
  3. pipを使ってインストール

このうち最後の「pipを使ってインストール」をするとcondaとpipのパッケージが混ざって厄介なことになる可能性がある。condaから入れたパッケージはpipからも認識されるものの、

  • 依存関係のバージョン違い
  • condaとpipのパッケージ名の違い(例: pyqt (conda) vs. PyQt5 (pip))

等から予期せずcondaのパッケージが上書きされてしまうことがある*1。その結果、パッケージ1つのインストールでAnaconda環境が壊れてしまい、Anacondaそのものを再インストールしない限り修復困難になってしまうことがある。また、condaがハードリンクを用いてパッケージを共有している関係から一つの環境でやらかしてしまったが最後、他の仮想環境まで破壊されることもある。

PyQtの場合実際これが起こりうる。こうなるとPyQt5をインポートしようとした瞬間、

ImportError: DLL load failed: The specified module could not be found.

というエラーが出たり、Python自体がSegmentation faultで強制終了・クラッシュしてしまう。こうなってしまうとPyQtをpipやcondaで再インストールしても修復不可能になる場合がある。(以下のGitHub上のIssue参照)
github.com
繰り返しになるが、直接PyQt5をインストールしなくても、依存関係を満たすために予期せずpipがパッケージを上書きしてしまう場合がある。この点が特に厄介だと感じた。

どうすれば良いか

今までの説明からconda install X でパッケージが見つからなかった場合に安易にpipから入れるのは危険だということがわかる。リスクを減らすためには例えば次の様な手順を踏む。

  1. anaconda search X でXを提供しているチャネルがないか探す。あればconda install -c channel X等の方法でインストールする(この場合もチャネルの優先順位など、様々な注意が必要。詳しくは公式ドキュメント参照)。
  2. pipから入れたい場合、まずPyPIのサイトから該当するパッケージを探し、依存関係を調べておく。依存するパッケージのうち、condaからインストール可能なものは予めインストールしておく。
  3. 依存関係を満たしたらpip install --no-deps XでXをインストールし、動作確認する。

あるいは別の選択肢として、

  • pipからしか入れられないパッケージを入れたい場合、新しいcondaの環境を作る(conda create -n env python)。その環境内ではconda installは一切用いない。
  • Anacondaを使うのをやめるPython公式サイトのPythonを使い、パッケージはpipで導入する。仮想環境についてはvenvvirtualenvを用いる。

自分のconda環境は大丈夫?

既に構築済みの自分のconda環境でpipとcondaの衝突があるか確かめたい場合は、conda listを実行する。同じパッケージがpip経由とconda経由で入っている場合重複して表示される。何かがおかしくなっている可能性が高い。

*1: pip installは既存のファイルを上書きするときに何の警告も出さないし、インストールする前にどのパッケージが導入されるか確認することも(おそらく)できない。このあたりはpipの作り込みの問題のような気がする。