えんでぃの技術ブログ

えんでぃの技術ブログ

ネットワークエンジニアの視点で、IT系のお役立ち情報を提供する技術ブログです。

Pythonパッケージの管理方法

python-logo

お伝えしたいこと

Pythonパッケージ管理で色々悩んだ結果、私が実践している方法を紹介します。

基本はvenvを使うことで環境をクリーンに保ちますが、VS Codeなどの外部ツールとライブラリを連携する際にvenvが問題となるケースもあります。
本記事では、そういったお悩みを解消するやり方を紹介します。

本記事で紹介する管理方法には、venvなどデフォルトで含まれている仕組みのみを利用しています。
本記事の管理手法を試すのに、追加のソフトウェア導入は不要です。

なお、本記事ではvenvの基本機能を理解していることを前提としています。
venvを全く知らない方にとっては解説が不十分かもしれませんので、必要に応じて別の情報ソースを検索いただければと思います。

サマリ

私が現在実践しているPythonパッケージ管理方法は、以下の通りです。

  • Pythonパッケージは、原則venv配下にインストールする
  • ただし、venvの外部から参照されるパッケージは以下のように管理する
    • 一般ユーザー権限のpip install~/.local/配下にインストールする
    • 必要なパッケージと理由を書いてrequirements.txtに保存しておく
    • Pythonパッケージ構成をリセットする便利コマンドを作っておく

原則、venv配下にパッケージをインストールする

Pythonパッケージをインストールする場合、特に理由がなければ原則としてvenv配下にインストールすることをおすすめします。

venvを使うことのメリットは、以下の2点だと思います。

  1. 環境を汚しにくい
  2. 最小限のパッケージ構成で開発できる

環境を汚しにくい

venvの実体は、いくつかの設定ファイルを含むディレクトリです。

venvを作成すると、いくつかのファイルやフォルダを含むディレクトリが生成します。
そしてvenvをactivateしてからPythonパッケージをインストールすると、venvディレクトリ配下にパッケージを利用するためのライブラリや実行ファイルがインストールされます1

最後に、venvが不要になったらvenvディレクトリごと削除することで、venv配下のPythonパッケージも含めて環境をリセットできます。

以下にコマンド例を示します。

お試しでvenvを作成し、任意のPythonパッケージを導入して開発を進めます。

$ mkdir ~/venv
$ python3 -m venv ~/venv/tmp
$ source ~/venv/tmp/bin/activate
(tmp)$ pip install ansible

venvが不要になったら、venvをdeactivateし、ディレクトリごと削除します。

(tmp)$ deactivate
$ rm -rf ~/venv/tmp

(参考) pip uninstallによる環境リセットは面倒

venvを使わなくてもpip install --userコマンドを実行した場合は~/.local/配下に、pip install --systemコマンドを実行した場合は/usr/local/配下にPythonパッケージファイルがインストールされます2
そして、pip uninstallコマンドでパッケージを1つずつアンインストールすることで環境を元に戻すことができます。

しかし、pip uninstalldnfなどのディストリビューション付属のパッケージマネージャとは異なり、依存パッケージをアンインストールしてくれません。

具体例を示します。

例えば、pip install ansible-lintを実行すると、依存関係にあるパッケージを複数インストールします。

# 最初は何もインストールされてない状態
pip list --user

pip install yamllint
# Successfully installed pathspec-0.8.1 yamllint-1.26.1

pip list --user
# Package  Version
# -------- -------
# pathspec 0.8.1
# yamllint 1.26.1

ここで、yamllintをアンインストールします。
これで元の状態に戻ってくれれば良いのですが、依存パッケージのpathspecは消えずに残ってしまいました。

pip uninstall yamllint
# Proceed (y/n)? y
#   Successfully uninstalled yamllint-1.26.1

pip list --user
# Package  Version
# -------- -------
# pathspec 0.8.1

元の状態に戻すには、以下のコマンドのように依存パッケージも含めて全て指定してアンインストールする必要があります。

pip uninstall yamllint pathspec

venvのディレクトリ一括削除と比べて、pip uninstallによる環境リセットには手間がかかることがご理解いただけたかなと思います。

最小限のパッケージ構成で開発できる

venvに入ると、venv外のパッケージが見えなくなります。
以下に具体例を示します。

venvの外でpip listを実行すると、システム全体やユーザー単位でインストールされたPythonパッケージが全て列挙されます。

pip list
# Package            Version
# ------------------ ----------
# argcomplete        1.12.3
# beautifulsoup4     4.9.3
# blivet             3.4.2
# (以下略)

ここでvenvを新規作成してactiavateし、再度pip listを実行します。
するとvenvの外のパッケージが見えなくなり、最小構成の状態になります。

$ python3 -m venv ~/venv/tmp
$ source ~/venv/tmp/bin/activate
(tmp)$ pip list
# Package    Version
# ---------- -------
# pip        21.2.3
# setuptools 57.4.0

デフォルト構成のLinuxでも、ディストリビューションのパッケージに付属してPythonパッケージが多数導入されています。
Python開発をする際に開発環境に不要なパッケージまでインストールされていると、開発したツールが動作するのに最低限必要なPythonパッケージがわかりにくくなってしまいます。
そうなってはREADME.mdにツールの動作要件を書くことができませんし、自分で使うときにも他の環境でツールを正しく実行できるか自信を持てなくなってしまいます。

venvを使うことで、Python開発環境をクリーンに保ちやすくなります。
開発プロジェクトの数だけvenvを作成し、それぞれ独立してパッケージを管理すれば不要なパッケージの存在に頭を悩ませることはなくなります。
そして、開発が一段落したらvenvをディレクトリごと削除すれば環境をリセットできます。

外部から参照されるPythonパッケージの管理方法

外部からの参照とは?

例えば、Ansible Playbook (ソースコード) をVS Code (エディタ) で編集する際にAnsible拡張機能を使うと様々な機能を利用できます3

機能の1つとしてエラーチェックがあるのですが、この機能を動作するには以下のPythonパッケージがインストールされている必要があります。

  • ansible-core (またはansible)
  • ansible-lint
  • yamllint

ここでVS Codeがvenv外で動作していて、上記3パッケージがvenv配下にインストールされている場合、「venv外からのPythonパッケージ参照」に該当します。

他にもVS CodePython拡張機能のLinter機能を利用したい場合など、似たようなケースは実際にいくつも存在します。

venv外からの参照はさせるべきではない

これらのPythonパッケージをvenv配下でインストールしたとしても、venv外で動作しているVS Codeから利用することはできません。
なぜなら、Python実行ファイルやPythonライブラリの検索先のディレクトリに上記3パッケージが含まれていないためです。

以下のようにいくつかの方法で無理やり動作させることはできるかもしれませんが、手間がかかる上に何かしら問題が起こることが多いです。

  • venv配下の実行ファイルをフルパス指定で実行する
  • venv配下でVS Codeを起動する
  • venvをPATHに追加する (venvの意味がほぼなくなりますが...)

問題が起こるのは、主に以下の原因からです。

  • 拡張機能の仕様として、実行ファイルのパスを指定できないことがある
  • 拡張機能ソースコード上で、実行ファイルのフルパスがハードコードされていることがある

したがってVS Codeのような外部からPythonパッケージを参照したい場合、そのPythonパッケージはvenv配下にインストールするべきではありません。

おすすめの対処法

pip install --userでインストールする

VS Codeなどの外部ツールからPythonパッケージを利用したい場合、venvを使わずにpip install --userでインストールするのが素直な方法です。
この方法であればPythonパッケージのインストール先がホームディレクトリ配下 (~/.local/) なので影響範囲が特定ユーザーに限定され、システム全体を汚しません。
また、venv配下ではないのでVS Codeのような外部ツールとの連携で困ることもありません。

venv外とは言っても、sudo pip installは実行しないでくださいね。
これを実行すると、色々と混乱の元になります。
参考: sudo pip を実行してはいけない理由

以下にコマンドの具体例を示します。

pip install --user ansible-lint yamllint ansible

この方法は、AnsibleのようなPythonパッケージベースのツールをシステムワイドではなく、ユーザー単位でインストールして気軽に使いたい時にも便利です。
よく使うコマンドであれば毎回venvに入るのも面倒なので、VS Code連携などの理由がなくてもこの方法でインストールしちゃって良いかもしれません。

しかし、venv外へのインストールは、前述の#(参考) pip uninstallによる環境リセットは面倒の通り、依存パッケージも含めて削除するのが面倒という弱点があります。
この弱点を軽減する方法を次のセクションで紹介します。

ユーザー権限でインストールしたパッケージを一括削除する

venvのディレクトリ削除相当として、pip list --userで表示されるPythonパッケージを全て削除するコマンドを紹介します。
以下のコマンドを実行すれば、依存パッケージを1つ1つ指定してアンインストールする必要はなくなります。

pip freeze --user | xargs pip uninstall -y

もちろんのことですが、このコマンドはユーザー配下とはいえ影響が大きいので実行する際は注意してくださいね。
またvenvディレクトリの削除と比較して、パッケージ数が多いと実行時間が長くかかります。

Pythonパッケージ構成のバックアップ

インストールしたパッケージは、requirements.txtに書いておくと、後々手軽に復旧できます。
必要な理由もセットでメモしておくと良いと思います。

requirements.txtの置き場所は任意ですが、venv外であれば例えば~/.local/lib/requirements.txtに置くのはいかがでしょうか。
venvの場合は、venvのディレクトリ配下に置くとわかりやすいと思います。

以下のようにインストールしたパッケージ名と共に理由もコメントで付記しておくと、後々経緯を追いやすいです。

# For Ansible VS Code Extension
ansible-lint
yamllint
ansible

上記パッケージをインストールしたいときは、pip install -r requirements.txtを実行します。

今回の例では、以下のコマンドになります。

pip install -r ~/.local/lib/requirements.txt

(参考) Pythonパッケージ構成をリセットするalias

ここから先は好みの世界になってきますが、私の環境では以下のaliasを~/.bashrcに登録しています。
pip_resetをただ実行するだけで、いつでもPythonパッケージ構成を決まった状態に戻せます。
※requirements.txtにPythonパッケージを指定していないと、毎回バージョンが変わってしまいますが...

あまり頻繁に使うことはありませんが、私の場合はこのaliasがあることで気軽にpip install --userを実行できるようになりました。

コマンドの構造は単純で、Pythonパッケージの一括削除コマンドと、requirements.txtからの復旧コマンドを;で連結しただけです。

alias pip_reset='pip freeze --user | xargs pip uninstall -y; pip install -r ~/.local/lib/requirements.txt'

よろしければお使いください。

まとめ

Pythonパッケージを気軽に導入できるように、自分なりに考えたことを共有しました。

「venvは使いたいけど、VS Codeの連携時にうまく行かずに困っている」といったケースにハマるソリューションとして、お役に立てれば幸いです。


  1. venvは、Pythonの仕組みによってvenv配下の実行ファイルやライブラリを優先的に参照するよう制御する仕組みです。venvの詳しい動作について気になる方は、venvが動作する仕組みを調べてみたをぜひご覧ください。

  2. sudo pip installpip install --systemは基本的に使うべきではありません。その理由は、sudo pip を実行してはいけない理由に書きました。

  3. VS CodeのAnsible拡張機能について、詳細はAnsibleユーザーのためのVS Code拡張機能の紹介をご覧ください