外部から管理される環境#

Python の導入方法の中には Python をインストールするユーザが全てを管理するものもある一方で、(Linux ディストリビューションのオペレーティングシステムが提供するパッケージマネージャや、専用のインストーラを伴うアプリケーションにおけるバンドルされた Python 環境のような) 別の手段で準備され管理されるものもあります。

そのような環境下で従来の Python のパッケージングツール類を使おうと試みることは、最もうまくいったときでも混乱を招く結果になり、最悪の場合には根底にあるオペレーティングシステム全体を完全に破壊してしまうことにもなりかねません。このような問題を解決する上では、説明文書と互換性ガイドだけが頼りになります。

この仕様では EXTERNALLY-MANAGED というマーカファイルを定義しており、これによって Python をインストールする際に pip のような Python に特有のツール群に対してインタプリタのデフォルトのインストール環境にパッケージをインストールしたり取り除いたりしないように指示し、その代わりに、エンドユーザに対して 仮想環境 <virtual-environments を使うようにガイドするべきです。

また、 Python 特有のパッケージマネージャがインタープリタ全体のコンテキストでパッケージをインストールしようとしている場合に、外部のパッケージマネージャと相互干渉を避け、また、外部のパッケージマネージャから導入されたソフトウェアを破壊する危険を避けるであろうやり方でインストールすることができるように sysconifg の翻訳を標準化しています。

用語集#

この仕様書で用いられる用語のいくつかには、それが置かれた文脈に従って複数の意味を持つものがあります。明確性のために、この仕様書では以下の用語を特定の意味で使用します:

配布物 <distro>

"配布物 <distribution>" の短縮系で、

配布物 <distro> は、Debian 、 Fedora や FreeBSD のようなそれ自身のオペレーティングシステム (OS) であっても構いません。あるいは、 Homebrew や MacPorts のような既存の OS の上に重ねて配布物をインストールするものであっても構いません。

Python のパッケージングの文脈では、"ディストリビューション <distribution>" という用語は別の意味、つまり、 Python 言語で書かれたソフトウェアのひとつの部品のソースコード配布パッケージまたはバイナリ配布パッケージ、即ち setuptools.dist.Distribution 乃至 "sdist" の意味での配布物という意味を持つので、この文書では短縮された用語である "ディストロ <distro>" を使います。混乱を避けるために、この文書では "ディストリビューション <distribution>" をそのままでは使わないことにします。 Python パッケージングの意味で使う時は完全形の "配布パッケージ <distribution package>" または単に "パッケージ <package>" を使います (後述します)。

ディストロを提供する者 - ソフトウェアを収集して公開し、何であれ必要とされる修正を加えるチームまたは企業 - は、その 配布者 <distributor> です。

パッケージ

インストールすることができ、Python 内で使うことができるソフトウエアの単位。つまり、これは Python 特有のパッケージングツールが 配布パッケージ または単に "配布物" と呼びがちなものを参照しています; 口語的な省略形である "配布物" は Python Package Index の意味で使われます。

この説明文書は "配布物 <package>" を Python モジュールを含んでいてインポート可能なものの名前という意味では使いませんが、多くの場合、 配布パッケージは同名のインポート可能な単一のパッケージから構成されています。

この説明文書では、"パッケージ <package>" という言葉で (.deb.rpm ファイルのような) ディストロのパッケージマネージャがインストールする単位を参照することはしません。必要な場合には "ディストロのパッケージ" などと言い換えて使います。 (再掲になりますが、多くの場合には、 Python パッケージがディストロのパッケージとして出荷される場合には Python パッケージの名称に python- を付け加えます。)

Python 特有のパッケージマネージャ

Python パッケージング標準 <Python packaging standard> を満足する形で Python パッケージをインストール・アップグレード・削除するためのツール。最もよく知られた Python 特有のパッケージマネージャは pip です; 他の例には、 setup.py コマンドを直接に使う方法と同様、古い Easy Install コマンド が含まれます。

(easy_install コマンドは、2021 年 1 月 23 日にリリースされた setuptools のバージョン 52 で除去されています。)

(Conda は少々特別なケースです、というのは、 conda コマンドを使えば単に Python パッケージだけではなくずっと多くのものをインストールすることができるからで、いくつかの意味ではディストロ <distro> 付属のパッケージマネージャにもっと似ていると言えるでしょう。 conda コマンドは一般的には Conda が作成した環境の上でのみ動作するので、Python 特有のパッケージマネージャとして動作する場合には、この説明文書における関心のほとんどは conda には適用されません。)

ディストロ <distro> パッケージマネージャ

当該ディストロのインストール済みインスタンス内でディストロ提供のパッケージをインストール・更新・削除するためのツールで、 Python パッケージか非 Python パッケージかに依らずインストールする能力を持つものであり、従って、一般的にそのツールに特有のインストール済みソフトウェアのデータベースで インストール済み配布物のデータベース とは無関係のものを持つもの。例には aptdpkgdefrpmpacmanbrew を含みます。顕著な特徴としては、あるパッケージがディストロのパッケージマネージャによってインストールされていた場合に、 Python 特有のパッケージマネージャを満足させる方法でそのパッケージを削除したり更新したりすることが、一般にディストロのパッケージマネージャの状態を不整合なまま放置するであろうということです。

この説明文書は、また、ある種の文脈ではディストロのパッケージマネージャのことを指し示すのに "外部パッケージマネージャ <external package manager>" や "システムのパッケージマネージャ <system's package manager>" のような言い回しを用います。

シャドー <shadow>

インストール済みの Python パッケージをシャドー <shadow> するというのは、シャドーされたパッケージに由来するファイルをひとつも削除することなしに、インポート時に別のパッケージを選好させることです。これは sys.path に複数のエントリーを要求します: パッケージ A 2.0 がある sys.path エントリに a.py モジュールをインストールし、かつ、パッケージ A 1.0 が a.py モジュールを後続の sys.path エントリにインストールしている時に、 import a が前者のモジュールを返し、これを A 2.0 が A 1.0 をシャドーしたと言います。

概要#

この仕様には二つの部分があります。

第一に、 ** Python インタープリタの配布を行う者が、そのインタープリタを Python の外側にある手段によって管理されるパッケージを持ち、従って、 pip のような Python 特有のツール類が、特別にオーバーライドされない限りは、当該インタープリタのグローバルな sys.path に存在するインストール済みパッケージを、いかなる意味でも変更するべきではないもの、つまり追加、更新・ダウングレード、あるいは削除を行うべきではないものとしてマークを付ける方法 ** を記述します。また、代替手段としての仮想環境をどのように使うのかを配布者 <distributor> が指示する手段を提供します。

これは、オプトインのメカニズムです: デフォルトでは、アップストリームのソースからコンパイルされた Python インタープリタは、そのようにマークを付けられることはなく、また、セルフコンパイルされたインタープリタやディストロのものを使って pip install を実行すると、それまでと同様に動作するはずです。

第二に、パッケージをインタープリタのグローバルコンテキスト (マークされていないインタープリタの場合やマークをオーバーライドした場合) にインストールする際に、 ** Python 特有のパッケージマネージャが修正・削除しても構わないのは、今まさにファイル群を作成しようとしている sysconfig スキームのディレクトリ内にあるものだけである ** というルールを設定します。これによって、 Python インタープリタの配布者 <distributor> がふたつのディレクトリをセットアップすることを許し、つまり、一つは自分で管理しているパッケージ群 (のためのディレクトリ) でもう一つはエンドユーザがインストールした管理対象外のパッケージ群 (のためのディレクトリ) で、管理対象外のパッケージをインストールしたとしても外部パッケージマネージャ <external package manager> が管理しているファイル群が削除 (または上書き) されることがないことを保証します。

外部パッケージマネージャ <external package manager> を用いるものとしてインタープリタにマークを付ける#

Python 特有のパッケージインストーラ (つまり pip のようなツール - apt のような外部ツールではない) がある Python コンテキストにパッケージをインストールする前に、デフォルトで以下の項目を確認するべきです:

  1. 仮想環境の外側で動作しているのか? sys.prefix == sys.base_prefix が成立するか否かで判断することができます。

  2. sysconfig.get_path("stdlib", sysconfig.get_default_scheme()) で同定されるディレクトリの中に EXTERNALLY-MANAGED ファイルが存在するか否か?

両方の条件が真であれば、仮想環境の外側ではこの Python インタープリタのディレクトリへパッケージをインストールすることが無効にされていることを示すエラーメッセージとともに、インストーラは終了するべきです。

インストーラは、コマンドライン上の --break-system-packages フラグのように、ユーザがこれらのルールをオーバーライドする方法を持っているべきです。このオプションはデフォルトでは無効であるべきで、有効にするとリスクを伴うことを含意する何らかのメッセージを伴うべきです。

EXTERNALLY-MANAGED ファイルは INI スタイルのメタデータファイルで、標準ライブラリの configparser モジュールでパースできることを意図しています。このファイルが UTF-8 エンコーディングを用いて configparser.ConfigParser(interpolation=None) によってパースできて、かつ、ファイル内に [externally-managed] セクションがあれば、インストーラはこのファイル内で指定されたエラーメッセージを探して、エラーの一部として出力するべきです。 locale.getlocale(locale.LC_MESSAGES) によって返されるタプルの最初の要素が None ではない場合には、 Error- の後に言語コードが続く名前を持つキーの値として提供されるエラーメッセージを探すべきです。そのようなキーが存在しない場合でも、言語コードがアンダースコアかハイフンを含むのであれば、 Error- の後に言語コードのアンダースコアかハイフンの前までの部分が続くキーを探すべきです。これらのいずれも発見できない場合や、言語コードが None の場合には、単に Error というキーを探すべきです。

インストーラがファイル内のエラーメッセージを見つけられない (ファイルをパースできないか、適切なエラーキーが存在しないのいずれか) 時は、インストーラは自身の内部に予め定義されたエラーメッセージ、それは当該パッケージをインストールするためにユーザが仮想環境を作成することを示唆するものであるべきですが、それを使うべきです。

Software distributors who have a non-Python-specific package manager that manages libraries in the sys.path of their Python package should, in general, ship an EXTERNALLY-MANAGED file in their standard library directory. For instance, Debian may ship a file in /usr/lib/python3.9/EXTERNALLY-MANAGED consisting of something like

[externally-managed]
Error=To install Python packages system-wide, try apt install
 python3-xyz, where xyz is the package you are trying to
 install.

 If you wish to install a non-Debian-packaged Python package,
 create a virtual environment using python3 -m venv path/to/venv.
 Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
 sure you have python3-full installed.

 If you wish to install a non-Debian packaged Python application,
 it may be easiest to use pipx install xyz, which will manage a
 virtual environment for you. Make sure you have pipx installed.

 See /usr/share/doc/python3.9/README.venv for more information.

パッケージのインストールを試みるユーザ向けに役に立つとともにディストロに適合した情報を適合すること。オプションとして、同一ファイル内に提供されるトランザクション:

Error-de_DE=Wenn ist das Nunstück git und Slotermeyer?

 Ja! Beiherhund das Oder die Virtualenvironment gersput!

作成後には更新できない単一アプリケーションのコンテナイメージのようなある種のコンテキストでは、配布者 <distributor> は、このルールを手動でオーバーライドせざるを得ないということがなくてもユーザが何であれ彼らの望むものを (その時に) インストールできるように、 EXTERNALLY-MANAGED ファイルを同梱しない選択を行っても構いません。

ターゲットとなる sysconfig スキームだけを書く#

通常は、 Python パッケージインストーラは、標準ライブラリパッケージの sysconfig によって返されるスキームにあるディレクトリへインストールを行います。普通は、これは、 sysconfig.get_default_scheme() が返すスキームのことですが、設定 (例えば pip install --user) に基づいて異なるスキームを用いることもあります。

インストーラが sysconfig スキームへインストールする時はいつでも、そのスキームの外側のファイルをインストーラが決して変更したり削除したりするべきではないことをこの仕様が宣言しています。例えば、パッケージのアップグレードを行う時にそのパッケージがスキームの外側のディレクトリ (おそらく別のスキームのもの) にすでにインストールされているなら、既存のファイル群をそのままにしておくべきです。

もし、アップグレードの最中にインストーラが既存のインストール (済のファイル群) をシャドーしてしまうのであれば、警告を生成して実行を中止することを推奨します。

インストーラが sysconfig スキームの外側の場所 (即ち pip install --target) へインストールしようとしているなら、このサブセクションは適用されません。

ディストロへの推奨#

この節は規範的なものではありません。特に他の理由がなければディストロが従うべきであると信じられるベストプラクティスを提供するものです。

インストールされたものを外部管理のものとしてマークする#

ディストロは、その stdlib ディレクトリ内に EXTERNALLY-MANAGED ファイルを作成するべきです。

仮想環境へ向かうユーザへのガイド文書#

このファイルは、ディストロのパッケージマネージャを使ってシステムワイドのパッケージをインストールする方法と仮想鑑賞を構築する方法の両方を示す、役に立ち、かつ、ディストロに適合したエラーメッセージを含んでいるべきです。もし、そのディストロが python3 コマンドは利用できる (かつ、とりわけ pipget-pip が利用できる) が、 python3 -m venv は動作しない状態で使われることが多いのであれば、当該メッセージは、 python3 -m venv を正しく動作させるにはどうすれば良いかをはっきりと示すべきです。

Python 言語で書かれたアプリケーションをインストールするツールである packaging pipx を採用して、エラーの中で示唆することを検討してください。 pipx はそのアプリケーションだけのための仮想環境を自動的に構築しますし、何らかの Python 言語で書かれたソフトウェア (であって、ディストロ側では利用可能ではないもの) をインストールしたいけれども自身は Python ユーザではないエンドユーザにとってはずっと良いデフォルト動作です。 Packaging pipx がディストロ側で準備されていれば、システム側のパッケージ群を破壊することを 避ける ために pip install --user --break-system-packages pipx を使うようにユーザに指示すると言う皮肉を避けることができます。このように整えることで、ディストロ側パッケージとエンドユーザ用 Python 環境 (例えば、 Fedora での python3 や Debian での python3-full ) がともに pipx に依存するようにすることができます。

コンテナイメージ内のマーカーファイルを維持する#

単一アプリケーションのコンテナ用の公式イメージ (即ち Docker コンテナイメージ) を作成するようなディストロは、 EXTERNALLY-MANAGED ファイルを維持すべきで、さらに言えば、ユーザがそのイメージなでパッケージのアップデートをインストールしたとしても (RUN apt-get dist-upgrade のようなものを考えてみてほしい) 失われることのないようなやり方で行うのが望ましいことです。

ディストロとローカルのディレクトリを別々に作成する#

ディストロは、ディストロがインストールしたパッケージのためのものと、ローカルのシステム管理者がインストールしたパッケージのためのものというふたつの別々のパスをシステム側に持っているインタープリタの sys.path に置くべきであり、また、 sysconfig.get_default_scheme() が後者のパスを指し示すように設定するべきです。こうすることで、pip のようなツール類がディストロ側でインストールしたパッケージを修正してしまうことがなくなります。ローカルのシステム管理者用のパスは sys.path 内でディストロ側のパスの前にくるべきで、そうすることでローカルにインストールされたものがディストロ側のパッケージよりも選好されるようになります。

例えば、Fedora や Debian (およびその派生物) は共に、ローカルにインストールされたパッケージ用に /usr/local を使い、ディストロがインストールしたパッケージ用に /usr を用いることでこの分離を実装しています。Fedora では、 /usr/local/lib/python3.x/site-packages/usr/lib/python3.x/site-packages です。 (Debian では /usr/local/lib/python3/dist-packages/usr/lib/python3/dist-packages をローカルにコンパイルされた Python インタープリタ用にさらに分離して使っています: もしアップストリームの Python をビルドして /usr/local/bin にインストールしていれば、 /usr/local/lib/python3/site-packages を見るでしょうし、 Debian ではローカルにビルドされたインタープリタ経由でインストールされたパッケージがディストロ側のインタープリタ用の sys.path 上には出現しないことを確実にしようと願っています。)

/usr/local/usr の分離が、 PATH 環境変数が典型的に /usr/local/bin:/usr/bin を含んでいて、デフォルトで非ディストロのソフトウェアが /usr/local にインストールされることのアナロジーになっていることに注目してください。この分離は、 推奨されるファイルシステム階層標準 です。

これを行うにはふたつの方法があります。ひとつは、Python ライブラリを直接にビルドしてパッケージしようとしている (例えば、使っているパッケージングヘルパーが wheel をアンパックするか、または、 setup.py install を呼び出す場合) 時に、 sys.path には含まれているけれども sysconfig スキームには入っていないようなディレクトリを使って実行するようにそれらツール群を調整することです。

もうひとつは、パッケージのビルド中とインストール済みのシステムで実行する時とでデフォルトの sysconfig スキームを変えるように調整することです。 (アクセプトされ実装されればですが) bpo-43976 からの sysconfig カスタマイズフック群がこれを容易にするでしょう: パッケージングツールに環境変数他のなんらかの検出可能な設定項目を設定させ、 get_preferred_schemes 関数がパッケージビルドの中から呼ばれた場合には異なるスキームを返すようにするのです。そうすれば、 pip install をディストロパッケージングの一部として使用できるようになります。

我々は、 pip が指定されたスキームで走るように指示する --scheme=... オプションを追加することを提案します。(現在の pip がスキームを決定する方法については後述の 実装ノート を参照してください。) これが利用可能になり次第、ローカルのテストやおそらく実際のパッケージングで、 (get_preferred_schemes をバイパスして) 明示的にディストロ側の場所へパッケージをインストールするために pip install --scheme=posix_distro のような形で実行することができるようになるでしょう。また、もし本当に必要であれば、 pip を使ってシステム側が管理するディレクトリからパッケージを削除するために pip uninstall --scheme=posix_distro を使うことさえできるでしょう。

pip でパッケージをインストールするには、 EXTERNALLY-MANAGED マーカーファイルを抑止して pip を実行できるようにするか、コマンドラインでオーバーライドするかのいずれかが必要です。コンテナイメージ内で作業している時は、ビルドのために chroot した環境においても同じ方法でマーカーファイルを抑止したくなるかもしれません。

これらの設定 (ビルド環境ではマーカーファイルを抑止し、 get_preferred_schemes が自動的にディストロのスキームを返すようにすること) を自動で行うことのアドバンテージは、素のままの pip install であってもパッケージビルド内で動作することで、それは一般的にはアップストリームのビルドスクリプトで内部で偶々 pip install を呼ぶものでも修正なしで正しく動作するということです。もちろん、パッケージングプロセスが常に pip install --scheme=posix_distro --break-system-packages を呼び出すようにすることもできますし、それも動作します。

ここでの最善のアプローチは、ディストロの慣習やパッケージング機構に大きく依存します。

同様に、 sysconfig パスでインポート可能な Python のコード向けのものではないもの - つまり、 includeplatincludescriptsdata - にもふたつの変種があって、ひとつはディストロ側でパッケージしたソフトウェア、もう一つはローカルにインストールされたソフトウェアですが、ディストロはこれらのどちらでも使用できるように設定されているべきです。例えば、典型的な FHS を遵守するディストロでは、 /usr/local/include をデフォルトのスキームの include 用に、 /usr/include をディストロ側でパッケージかしたヘッダファイル用に使って、両方をコンパイラのサーチパスに含めておくことになるでしょうし、また、 /usr/local/bin をデフォルトのスキームの scripts 用に、 /usr/bin をディストロ側のパッケージのエントリポイント用に使って、両方を $PATH に含めておくでしょう。

実装に関する覚書#

この節は規範的なものではなく、仕様と潜在的な実装の両方に関連する覚え書きを含んでいます。

現在 (2021年5月時点) 、 pip はターゲットとなる sysconfig スキームを選択する方法を直接には外部に見せていませんが、インストールの際にルックアップする3個の方法があります:

pip install

sysconfig.get_default_scheme() を呼び出す方法で、 (アップストリームの CPython や今使われているほとんどのディストロでは) 普通は get_preferred_scheme('prefix') と同じ結果になるもの。

pip install --prefix=/some/path

sysconfig.get_preferred_scheme('prefix') を呼び出す。

pip install --user

sysconfig.get_preferred_scheme('user') を呼び出す。

最後に、 pip install --target=/some/path でスキームをルックアップすることなしに /some/path を書きます。

Debian は、現在、 (VIRTUAL_ENV 環境変数を確認することを含む) いくつかの発見的な解法を使った 仮想環境内のデフォルトのインストール先を変更するパッチ を入れていて、仮想環境内で使われるディレクトリが dist-packages ではなく site-packages に留まるようになっています。これは、この提案に特に影響を与えません、というのは、そのパッチの実装は、実際にはデフォルトの sysconfig スキームを変更するわけではなく、特に sysconfig.get_path("stdlib") の結果を変更しないからです。

Fedora は、現在、rpmbuild 内で実行しているのでなければデフォルトのインストール先を変更するパッチ を適用していて、「ふたつのシステムワイドなディレクトリ」のアプローチを使っています。これは、概念としては、 sysconfig スキームを変更するものとしてではなく distutils へのソースコードパッチとして実装されていることを除けば、 bpo-43976 によって可視化されたある種のフックです。

上記の is_virtual_environment の実装は、 EXTERNALLY-MANAGED ファイルをロードしてそこからエラーメッセージを探す論理と同様に、実装を中央一括管理にするために標準ライブラリ (順に syssysconfig) に追加されるかもしれませんが、まだそのようにはなっていません。

歴史#

  • 2022年6月: PEP 668 を通じてこの仕様が承認されました。