Pythonの魅力の1つが豊富なパッケージ群を持っていることだ。しかし、その多種多様なパッケージを適切に管理する方法についてはよく考える必要がある。ほとんどのPythonパッケージはPython Package Index(PyPI)と呼ばれるリポジトリに登録されているが、問題なのはこのリポジトリから実際にパッケージを取得してインストールする方法だ。
一般的には、何らかのパッケージマネージャーを利用して一元管理するのが望ましい。しかし具体的にどのパッケージマネージャーを利用するのが最適なのだろうか。本稿では、Opensource.comの記事「Managing Python packages the right way」を参考に、Pythonのパッケージを管理するためのベストプラクティスを紹介しよう。
グローバルインストールで留意すべき点
pipは優れたPython用のパッケージマネージャーであり、Pythonユーザーの間ではデファクトスタンダードになっている。pipを使えば、インストール前に依存関係の解消や重複インストールのチェックなどを自動で行ってくれるため、安全なインストールが行える。しかしpipによるインストールは、デフォルトではシステム全体で共有されるグローバルな場所に対して行われる点に注意しなければならないと、Opensource.comは指摘している。
(※ただし、このデフォルトの挙動はディストリビューションによっても異なる。例えば、Debien版やUbuntu版のpipの場合、非root権限で実行されたpip installは自動的に後述するuserスキームモードは使用されるようになっている。)
グローバルインストールの問題のひとつは、特定のPythonインタプリタに対して、一度に複数のバージョンのパッケージがインストールできない点だという。このことは、依存関係によって異なるバージョンのパッケージが要求される場合に問題を引き起こす可能性がある。
その他の潜在的な問題としては、aptやdnfなどのOS標準のパッケージマネージャが、一部のユーザがアクセス権限を持たない場所にPythonパッケージをインストールしている可能性がある。この場合、書き込み権限を持たないディレクトリが依存パッケージのインストール先になっている場合などに、ユーザー権限で実行された pip install は失敗してしまう。
技術的にはsudoコマンドを使用してroot権限で pip install を実行すれば権限の問題は回避できる。しかしそうすると、今度は、OS標準のパッケージマネージャーが管理する場所に対してpipがパッケージをインストールすることになり、パッケージ管理の一貫性が崩れるという別の問題が発生してしまう。
root権限でpip installを使うセキュリティ上のリスク
root権限でのpip installが推奨されない理由はほかにもあるという。昨今の多くのPythonライブラリとアプリケーションは、ビルドシステムとして「setuptools」を使用している。setuptoolsを使用するにはプロジェクトルートにメタデータを記述したsetup.pyファイルが必要だが、このファイルにはビルドプロセスをカスタマイズするための任意のPythonコードを含めることができる。
もしsetup.pyにバグや悪意のあるコードが含まれていた場合、これをroot権限で実行する危険性は説明するまでもないだろう。PyPIリポジトリには誰でもパッケージを登録することができ、現時点ではまだそれを効果的にレビューする仕組みもないため、気づかないうちに悪意のあるsetup.pyを実行させられるリスクは誰にでもあるという。ただし、Python Software Foundation(PSF)はPyPIのセキュリティを強化する取り組みを進めているため、将来的にはこのようなリスクは解消されることが期待できる
いずれにしても、root権限でのpip installはすべての依存関係に関する問題の解決策にはならないため、代替案を考える必要があるとのことだ。
OS標準のパッケージマネージャーを使用する
pipを使用しない場合の代替案のひとつとしては、OS標準のパッケージマネージャーの使用が挙げられている。Linuxであればaptやdnf、pacmanなど、maxOSであれば(OS標準というわけではないが)Homebrewなどがある。これらのパッケージマネージャの使用については、一長一短があるという。
まず長所は、パッケージマネージャ経由でインストールされるソフトウェアは一般的に安定版が使用されるため、対象のプラットフォームでより適切に動作する場合が多いという点である。その一方で短所としては、バージョンの選択肢がpipを使用する場合よりも少なく、またアップストリームへの追随が遅れる可能性がある。
pipを使用するのか、OS標準のパッケージマネージャーを使用するのかは、その環境で要求されるニーズに応じて決定する必要がある。
pipのuserスキームモードを使用する
root権限でのpip installの実行問題を回避するために、pipをuserスキームモードで使用するという選択肢もあるという。userスキームはPython 2.6から導入されたもので、これによってパッケージをユーザーが権限を所有する場所にインストールすることができる。
ファイルは、site.USER_BASE で指定されるパス(以下、[userbase]と記述)にインストールされる。具体的には、UNIX環境であれば、モジュールは「[userbase]/lib/pythonX.Y/site-packages」に、スクリプトは「[userbase]/bin」に、データは「[userbase]」にそれぞれ配置される。
次のコマンドは、userスキームモードを使用してblackというパッケージをインストールした例である。この場合、blackのスクリプトは [userbase]/bin ディレクトリにインストールされる。
$ python3 -m pip install --user black
Collecting black
Downloading black-20.8b1.tar.gz (1.1 MB)
|████████████████████████████████| 1.1 MB 5.0 MB/s
Installing build dependencies ... done
Getting requirements to build wheel ... done
Preparing wheel metadata ... done
(中略)
WARNING: The scripts black, black-primer and blackd are installed in '/home/takasyou/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed appdirs-1.4.4 black-20.8b1 click-7.1.2 mypy-extensions-0.4.3 pathspec-0.8.1 regex-2020.11.13 typed-ast-1.4.1 typing-extensions-3.7.4.3
$
userスキームモードを使用すればディレクトリ権限の問題は解消されるため、root権限でpip installを実行する必要はなくなる。ただしこの場合でも、同じパッケージの異なるバージョンが同時にインストールできないという問題は解決できない。
仮想環境を利用する
ひとつのシステム上に、仮想環境を利用してプロジェクトごとにそれぞれ独立したPython環境を構築するという方法もあるという。この方法であれば、アプリケーション別の依存関係の競合を心配することなく、自由にパッケージをインストールすることができる。
Python 3.3からは、軽量な仮想環境を簡単に作成することができるvenvモジュールが標準ライブラリに追加された。これによって、コマンドひとつで新しい仮想環境を作成し、そこに独立したPythonプラットフォームを構築することができるようになった。
次のコマンドは、venvを使って「myenv」という名前の仮想環境を作成した例になる。
$ python3 -m venv myenv
新しい仮想環境を作成したら、その環境のbinディレクトリにあるactivateスクリプトを実行して環境をアクティブ化する。すると、サブシェルが起動してbinディレクトリをPATH環境変数に追加され、バイナリとスクリプトが実行できるようになる。
$ source ./myenv/bin/activate
(myenv) $
これ以降のコマンドは、すべて仮想環境myenvのPythonプラットフォームで実行される。例えば次のコマンドでは、myenvの環境内にblackスクリプトがインストールされる。
(myenv) $ python3 -m pip install black
whichコマンドで調べると、blackは次のようにmyenv以下のbinディレクトリにインストールされたことが確認できる。
(myenv) $ which black
/home/USERNAME/myenv/bin/black
仮想環境を非アクティブにするには、次のようにdeactivateスクリプトを実行すればよい。
(myenv) $ deactivate
$
なお、venvにインストールされたスクリプトは、仮想環境内のPythonインタープリタを使用するようにshebang行が書き換えられるため、アクティブ化を使用しなくても、システム上のどこからでも問題なくスクリプトを実行することができる。次の例では、myenvが非アクティブな状態で、myenv/bin以下にインストールされたblackスクリプトを直接実行している。
$ ./myenv/bin/black --version
black, version 20.8b1
仮想環境はPythonパッケージ管理のセキュリティや依存関係に関する多くの問題を解消できるため、Python開発では一般的に使用されているという。
Condaを利用する
CondaはAnacondaディストリビューションで提供される各種パッケージをインストールすることができる、クロスプラットフォームのパッケージマネージャーである。もともとPythonを使用するデータサイエンティストが直面するパッケージの依存関係の問題を解消するために開発されたという経緯があるため、データサイエンティストには特に人気があるパッケージマネージャーとなっている。
Condaによるパッケージのインストールでは、ユーザーの現在の環境やインストールされているパッケージ、ユーザーが指定するバージョンの制限などを分析し、互換性のある依存関係をインストールする方法を特定する。そのため、インストール時に他のパッケージの依存関係を破損するリスクを回避することができる。
依存関係の解消方法という点では優れているCondaだが、pipと比較するとパッケージの選択肢がはるかに少ないのが大きなデメリットである。
まとめ
Pythonを利用していて、パッケージ管理にまつわる(主に依存関係の)問題に遭遇したユーザーも少なくないはずだ。適切なパッケージ管理のための選択肢はひとつではないが、それぞれのニーズに応じて適切なツールを使用することで、より快適にPythonを利用できるようになるだろう。