pip install --upgrade pip (10.0.0) 後の奇妙な挙動について
TL;DR
pipを10.0.0に更新した後に
~$ pip Traceback (most recent call last): File "/usr/bin/pip", line 9, in <module> from pip import main ImportError: cannot import name main
というエラーが出た場合の対処方法をこちらにまとめた。
こちらの記事で言及したが、pipの挙動がちょっとおかしい。少し調べてみたところ原因がわかったので共有する。一応概要も載せておく。
概要
debian9でaptでpipを導入したあとに pip install --upgrade pip
を行った場合、pipが使えなくなる。具体的には
~$ pip Traceback (most recent call last): File "/usr/bin/pip", line 9, in <module> from pip import main ImportError: cannot import name main
のようにエラーがでて、pipコマンドが使えなくなる。
また、python-pip
を消して手動でインストールしても pip install
でパッケージがインストールされない。debian9またはubuntu17.10を使用しているユーザーは特にハマりやすい。
原因
それぞれの現象の原因を詳しく示す。
ImportErrorの原因
aptでインストールした場合は9.0.1がインストールされるが、pipの最新版は10.0.0である (リリースされたのはつい先日のことである) 。
9系のpipスクリプトの内容は次のようになっている。
pipでインストールしたもの
#!/usr/bin/python # -*- coding: utf-8 -*- import re import sys from pip import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit(main())
aptでインストールしたもの
#!/usr/bin/python # GENERATED BY DEBIAN import sys # Run the main entry point, similarly to how setuptools does it, but because # we didn't install the actual entry point from setup.py, don't use the # pkg_resources API. from pip import main if __name__ == '__main__': sys.exit(main())
どちらも pip.main
をそのまま実行していることがわかる。またaptはpipのスクリプトをそのまま使うのではなく、ディストリビューション向けにカスタマイズされている。
次に pip install pip
で自動的にインストールされる最新版10.0.0のpipスクリプトの内容を見てみると、pip._internal.main
を実行していることがわかる。
#!/usr/bin/python # -*- coding: utf-8 -*- import re import sys from pip._internal import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit(main())
実際にpythonのライブラリが配置されているディレクトリ構造を確認してみると、明らかに構成が変わっていることがわかる。
9.0.1
~$ ls /usr/lib/python2.7/dist-packages/pip commands models req vcs basecommand.py baseparser.py cmdoptions.py download.py exceptions.py index.py __init__.py locations.py __main__.py pep425tags.py status_codes.py wheel.py compat operations utils _vendor basecommand.pyc baseparser.pyc cmdoptions.pyc download.pyc exceptions.pyc index.pyc __init__.pyc locations.pyc __main__.pyc pep425tags.pyc status_codes.pyc wheel.pyc
10.0.0
~$ ls .local/lib/python2.7/site-packages/pip _internal _vendor __init__.py __init__.pyc __main__.py __main__.pyc
これに加えてpythonのライブラリが参照されるパスの順序は、$HOME/.local/lib/python2.7/site-packages
の方が /usr/lib/python2.7/dist-packages
よりも優先度が高い。
~$ python -c 'import sys; print sys.path' ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-x86_64-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/home/icchy/.local/lib/python2.7/site-packages', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/gtk-2.0']
そのため、import pip
で参照されるpipは $HOME/.local/lib/python2.7/site-packages/pip
(10.0.0) を指している。しかしaptでpipがインストールされている場合、pipコマンドで実行されるスクリプトは先に示した通り pip.main
であり、これは10.0.0においては存在しない。
つまり最初の ImportError
はpip9を想定しているスクリプトがpip10のライブラリを使おうとしたことによって発生したものである。
この問題についてのissueは上がっているのだが、メンテナーの考えとしては 「_internal
に全ての機能を移動させたのにはそれなりの理由があって、pip.main
はpip10では絶対にサポートしない」というものらしい。
おそらくディストリビューションのパッケージ更新によってpipスクリプトがアップデートされるのを待つしかないと思う。もちろん先に自分でpipスクリプトに手を加えて、pip._internal
を使うようにするのはアリだとは思う。
pip installが動かない原因
まずDebian9 (stretch), Ubuntu 17.10 (artful) 以降の python-pip
パッケージは 9.0.1-2
である。
python-pip 9.0.1-2からパッケージのsourceを取得して展開してみると、debian/patches/set_user_default.patch
という名前のパッチが存在し、パッチの説明には以下のような記述がある。
When running as a normal user in a non-virtual environment, default to --user and --ignore-installed. When inside virtual environments or when running as root, keep the default behavior.
つまりvirtualenv環境下でない場合は、デフォルトで --user
フラグ ($HOME/.local/ 以下にインストールするオプション) を追加する。これにより、このバージョンの python-pip
を使用していると pip install
がほとんど成功するようになるため1 、sudoをつけずともインストールされるのが当たり前という感覚になってしまう。
一方pip10ではinstall時にverbosity levelを2以上 (-vv
) にしないとエラーメッセージが見えなくなった。これはバグと認識されていて、どうやら返り値のチェックを EACCES
ではなく EPERM
にしてしまったことが原因らしい。
そのため、aptの python-pip 9.0.1-2 ユーザーがpip10を使うと pip install
はいつも通り --user
で実行されたものだと勘違いしてしまい、エラーメッセージも特に出ないのでパッケージがインストールされたように見えてしまうわけである。もちろんよくメッセージを見ると Successfully installed [package名]
というメッセージが最後に出ていないので、インストールが正常に終わっていないことがわかるのだがまあなかなか気づきにくいと思う。
解決策
これらに対していくつかの解決策を考えた。
pip9を使い続ける
システムのpipに手を加えるのは少々危険が伴う。もし既存のパッケージに一切影響を与えたくないならば、pip9をそのまま使い続けるのが一つの手である。
set_user_default.patch
が当たってるパッケージを使っている場合、もし pip install pip
を実行してしまうと $HOME/.local/lib
にpip10.0.0がインストールされてしまうため、例のバージョン違いによってpipが起動しなくなる。この場合は pip install pip==9.0.1
などを実行してインポートされるpipパッケージのバージョンを揃えておくと良い。
pip10に乗り換える
aptの python-pip
を消して、/usr/local/bin/pip
にpipをインストールする。pip10のバグによりインストール失敗のメッセージが見えなくなってしまうが、alias pip='pip -vv'
とでもしておくか、普段から気をつければまあ問題はない。
python-pip
で入れたpipでインストールするのはなるべく避けた方が良くて、公式が推奨しているget-pip.pyを使うのがよい。先ほど述べたようにインストール失敗のメッセージが出ない上に、本来pipはデフォルトのインストール先に /usr/local/
以下を使うので、システムにインストールする場合はsudoをつけるのを忘れないようにする。もちろん -vv
を使えばエラーは見える。
~$ python get-pip.py -vv Created temporary directory: /tmp/icchy/pip-ephem-wheel-cache-nCRTal Created temporary directory: /tmp/icchy/pip-install-n4gtqZ Collecting pip ... Installing collected packages: pip Traceback (most recent call last): File "/tmp/icchy/tmp_NGCSM/pip.zip/pip/_internal/commands/install.py", line 335, in run use_user_site=options.use_user_site, File "/tmp/icchy/tmp_NGCSM/pip.zip/pip/_internal/req/__init__.py", line 49, in install_given_reqs **kwargs File "/tmp/icchy/tmp_NGCSM/pip.zip/pip/_internal/req/req_install.py", line 748, in install use_user_site=use_user_site, pycompile=pycompile, File "/tmp/icchy/tmp_NGCSM/pip.zip/pip/_internal/req/req_install.py", line 961, in move_wheel_files warn_script_location=warn_script_location, File "/tmp/icchy/tmp_NGCSM/pip.zip/pip/_internal/wheel.py", line 314, in move_wheel_files clobber(source, lib_dir, True) File "/tmp/icchy/tmp_NGCSM/pip.zip/pip/_internal/wheel.py", line 285, in clobber ensure_dir(destdir) File "/tmp/icchy/tmp_NGCSM/pip.zip/pip/_internal/utils/misc.py", line 86, in ensure_dir os.makedirs(path) File "/usr/lib/python2.7/os.py", line 157, in makedirs mkdir(name, mode) OSError: [Errno 13] Permission denied: '/usr/local/lib/python2.7/dist-packages/pip-10.0.0.dist-info' Cleaning up...
システムのpipに変更を加えずにpip10を使う
pipコマンドで起動するのがそもそもの原因なので、python -m pip
でpipを起動すればなんら問題はない。
alias pip='python -m pip'
この場合システムのpipはどうなるんだと思うかもしれないが、$HOME/.local/lib
をパッケージのパスに追加しているのでrootの場合は /usr/lib/python2.7/dist-packages/pip
が参照される。つまり通常ユーザーはpip10を使い、システムはpip9を使うという奇妙な状態になる。もちろん各パッケージ自体はpythonのバージョンにしか依存しないので、pipのバージョンによって異なるパッケージが入ることはまずない。
まとめ
この問題の根本的な原因はpip10でディレクトリ構成の変更が行われたことにあった。変更にはそれなりの理由があって、pip側で解決策が打たれることはおそらくない。ディストリビューションのアップデートを待つか、自分で対応するしかなさそうというのが現状である。
解決策としてはこちらに示した3通りの方法が考えられる。他にも良い解決策があれば是非教えてほしい。
余談
pipのドキュメントは https://pip.pypa.io/en/stable/ で見られる。 pip 9.0.1のドキュメントを見るためにstableの部分を差し替えて https://pip.pypa.io/en/9.0.1/ にアクセスしたのだが、なんと403になっていた。9.0.3や10.0.0も同様なので、おそらくstableにしかアクセスできないようにして、そのシンボリックリンクとかを最新版に向けてるとかそんなところだと思う。
-
通常
--user
をつけない場合は/usr/local/以下にインストールしようとするため、root権限がないと失敗する。↩