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

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 というキーを探すべきです。

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

Python 特有のものではないパッケージマネージャを持つソフトウェア配布者

[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 に依存するようにすることができます。

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

Distros that produce official images for single-application containers (e.g., Docker container images) should keep the EXTERNALLY-MANAGED file, preferably in a way that makes it not go away if a user of that image installs package updates inside their image (think RUN apt-get dist-upgrade).

Create separate distro and local directories#

Distros should place two separate paths on the system interpreter's sys.path, one for distro-installed packages and one for packages installed by the local system administrator, and configure sysconfig.get_default_scheme() to point at the latter path. This ensures that tools like pip will not modify distro-installed packages. The path for the local system administrator should come before the distro path on sys.path so that local installs take preference over distro packages.

For example, Fedora and Debian (and their derivatives) both implement this split by using /usr/local for locally-installed packages and /usr for distro-installed packages. Fedora uses /usr/local/lib/python3.x/site-packages vs. /usr/lib/python3.x/site-packages. (Debian uses /usr/local/lib/python3/dist-packages vs. /usr/lib/python3/dist-packages as an additional layer of separation from a locally-compiled Python interpreter: if you build and install upstream CPython in /usr/local/bin, it will look at /usr/local/lib/python3/site-packages, and Debian wishes to make sure that packages installed via the locally-built interpreter don't show up on sys.path for the distro interpreter.)

Note that the /usr/local vs. /usr split is analogous to how the PATH environment variable typically includes /usr/local/bin:/usr/bin and non-distro software installs to /usr/local by default. This split is recommended by the Filesystem Hierarchy Standard.

There are two ways you could do this. One is, if you are building and packaging Python libraries directly (e.g., your packaging helpers unpack a wheel or call setup.py install), arrange for those tools to use a directory that is not in a sysconfig scheme but is still on sys.path.

The other is to arrange for the default sysconfig scheme to change when running inside a package build versus when running on an installed system. The sysconfig customization hooks from bpo-43976 should make this easy (once accepted and implemented): make your packaging tool set an environment variable or some other detectable configuration, and define a get_preferred_schemes function to return a different scheme when called from inside a package build. Then you can use pip install as part of your distro packaging.

We propose adding a --scheme=... option to instruct pip to run against a specific scheme. (See Implementation Notes below for how pip currently determines schemes.) Once that's available, for local testing and possibly for actual packaging, you would be able to run something like pip install --scheme=posix_distro to explicitly install a package into your distro's location (bypassing get_preferred_schemes). One could also, if absolutely needed, use pip uninstall --scheme=posix_distro to use pip to remove packages from the system-managed directory.

To install packages with pip, you would also need to either suppress the EXTERNALLY-MANAGED marker file to allow pip to run or to override it on the command line. You may want to use the same means for suppressing the marker file in build chroots as you do in container images.

The advantage of setting these up to be automatic (suppressing the marker file in your build environment and having get_preferred_schemes automatically return your distro's scheme) is that an unadorned pip install will work inside a package build, which generally means that an unmodified upstream build script that happens to internally call pip install will do the right thing. You can, of course, just ensure that your packaging process always calls pip install --scheme=posix_distro --break-system-packages, which would work too.

The best approach here depends a lot on your distro's conventions and mechanisms for packaging.

Similarly, the sysconfig paths that are not for importable Python code - that is, include, platinclude, scripts, and data - should also have two variants, one for use by distro-packaged software and one for use for locally-installed software, and the distro should be set up such that both are usable. For instance, a typical FHS-compliant distro will use /usr/local/include for the default scheme's include and /usr/include for distro-packaged headers and place both on the compiler's search path, and it will use /usr/local/bin for the default scheme's scripts and /usr/bin for distro-packaged entry points and place both on $PATH.

Implementation Notes#

This section is non-normative and contains notes relevant to both the specification and potential implementations.

Currently (as of May 2021), pip does not directly expose a way to choose a target sysconfig scheme, but it has three ways of looking up schemes when installing:

pip install

Calls sysconfig.get_default_scheme(), which is usually (in upstream CPython and most current distros) the same as get_preferred_scheme('prefix').

pip install --prefix=/some/path

Calls sysconfig.get_preferred_scheme('prefix').

pip install --user

Calls sysconfig.get_preferred_scheme('user').

Finally, pip install --target=/some/path writes directly to /some/path without looking up any schemes.

Debian currently carries a patch to change the default install location inside a virtual environment, using a few heuristics (including checking for the VIRTUAL_ENV environment variable), largely so that the directory used in a virtual environment remains site-packages and not dist-packages. This does not particularly affect this proposal, because the implementation of that patch does not actually change the default sysconfig scheme, and notably does not change the result of sysconfig.get_path("stdlib").

Fedora currently carries a patch to change the default install location when not running inside rpmbuild, which they use to implement the two-system-wide-directories approach. This is conceptually the sort of hook envisioned by bpo-43976, except implemented as a code patch to distutils instead of as a changed sysconfig scheme.

The implementation of is_virtual_environment above, as well as the logic to load the EXTERNALLY-MANAGED file and find the error message from it, may as well get added to the standard library (sys and sysconfig, respectively), to centralize their implementations, but they don't need to be added yet.

歴史#

  • June 2022: This specification was approved through PEP 668.