プラットフォームの互換性タグ#
プラットフォーム相互互換性タグは、特定のプラットフォームと互換であるとビルドツールが配布物にマークを付けることができるようにし、インストーラが自身が動作しているシステムと互換であるのはどの配布物であるかを理解できるようにします。
概要#
タグの書式は、 {python tag}-{abi tag}-{platform tag}
です。
- python タグ
'py27' ・ 'cp33'
- abi タグ
'cp33dmu' ・ 'none'
- プラットフォームタグ
'linux_x86_64' ・ 'any'
例えば、py27-none-any
というタグは、abi に対する要求なしに任意のプラットフォームで Python 2.7 (任意の Python 2.7 実装) との互換性を持つことを意味します。
wheel
ビルド済みパッケージのフォーマットは、ファイル名の中にこのようなタグを {distribution}-{version}(-{build tag})?-{python tag}-{abitag}-{platform tag}.whl
の形で包含しています。他のパッケージフォーマットにはそれぞれ独自の慣習があるかもしれません。
どのようなタグであれ、その中に潜在的に含まれる空白文字は _
で置換されるべきです。
Python タグ#
Python タグは、配布物が必要とする実装やバージョンを示します。主要な実装には短縮系のコードがあって、当初は:
py: 一般的な Python (実装に特有な機能を要求しません)
cp: CPython
ip: IronPython
pp: PyPy
jy: Jython
他の Python 実装には sys.implementation.name
を用いるべきです。
バージョンは py_version_nodot
です。 CPython は no dot にせずに済ませますが、必要な場合には代わりにアンダースコア文字 _
が使用されます。 PyPy は、おそらく、ここに pp18
や pp19
といったそれ自身のバージョンを使います。
多くの純 Python 配布物では、バージョンは 2
や 3
や py2
・ py3
といったメジャーバージョンだけにしておくことができます。
重要なことは、 py2
や py3
のような major-version-only タグは py20
や py30
の短縮形であるわけではないということです。そうではなくて、これらのタグは、パッケージ作成者がいくつものバージョンに互換性を持つ配布物を意図的にリリースしたのです。
単一ソースで Python 2/3 に互換性を持つ配布物は、合成タグ py2.py3
を用いることができます。 後述の タグの圧縮された組み合わせ を参照してください。
ABI タグ#
ABI タグは、いずれかの拡張モジュールによってどのような Python ABI が要求されているのかを示します。実装依存の ABI 群については、実装 (の名前) は、たとえば cp33d
がデバッグオプション付きの CPython 3.3 ABI のことを指すように、 Python タグと同様のやり方で短縮したものを使います。
CPython の安定版 ABI のことは、共有ライブラリの拡張子と同様に abi3
と書きます。
非常に不安定な ABI を持つ実装は、そのソースコードのリビジョンやコンパイラフラグその他の SHA-256 ハッシュ値の最初の6バイトを (BASE64にエンコードされた8文字として) 用いても構いませんが、おそらくはバイナリ配布物を配布することには大きな需要はないでしょう。それぞれの実装のコミュニティで ABI タグをどのように用いるのが最適かを決定すればよいでしょう。
プラットフォームタグ#
基本的なプラットフォームタグ#
最も単純な形式では、プラットフォームタグは、 sysconfig.get_platform()
(の出力) のすべてのハイフン -
と ピリオド .
をアンダースコア _
で置き換えたものです。 Python 3.12 における distutils の削除があるまでは、これは distutils.util.get_platform()
でした。例えば:
win32
linux_i386
linux_x86_64
manylinux
#
上記のような単純なスキームでは、Linux プラットフォーム向けに公開するための wheel ファイルの配布物用としては不十分で、それは、 Linux プラットフォーム群が巨大なエコシステムであって、プラットフォーム間に微妙な際が見られるためです。
代わりに、そのようなプラットフォーム向けには、 manylinux
という標準が Linux プラットフォームの共通のサブセットを体現していて、 manylinux
プラットフォームタグを付けて wheel をビルドすることで一般的な Linux のディストリビューションのほとんどで使えるようにすることができます。
現在の標準は、将来性が約束された manylinux_x_y
標準です。 manylinux_x_y_arch
の形でタグを定義していて、 x
と y
には glibc のメジャーバージョンとマイナーバージョンがサポート (例えば manylinux_2_24_xxx
であれば、 glibc 2.24+ を使うディストロならどれでも動作するはず) されており、 arch
はアーキテクチャで、上記の "simple" 形式での sysconfig.get_platform()
の値に合致するものです。
この後に述べる古めのタグ群は、後方互換性のために今もサポートされています:
manylinux1
は、x86_64
とi686
のアーキテクチャで libc 2.5 をサポートしています。manylinux2010
は、x86_64
とi686
上で libc 2.12 をサポートしています。manylinux2014
は、x86_64
・i686
・aarch64
・armv7l
・ppc64
・ppc64le
・s390x
上で glibc 2.17 をサポートしています。
一般に、古めのバージョンの仕様向けにビルドされた配布物には前方互換性があります (というのは、 manylinux1
の配布物はより新しいシステムでも同様に動作するはずだということです) が、後方互換性はありません (manylinux2010
の配布物が 2010 年よりも前に存在していたプラットフォームで動作するとは想定されていないということです) 。
manylinux1
と manylinux2010
は既に end-of-life に達していて、提供されているビルド環境にはもはやセキュリティアップデートが提供されることはないであろうという警告されていることもあって、パッケージ保守者は最も互換性のある仕様をターゲットにするように努力するべきです。
適切なプロジェクトがサポートするさまざまな manylinux
標準の最低限のバージョンを次に掲げる表に示します:
ツール |
|
|
|
|
---|---|---|---|---|
pip |
|
|
|
|
auditwheel |
|
|
|
|
musllinux
#
musllinux
ファミリーに含まれるタグは manylinux
に似ていますが、 glibc ではなくて musl_libc を使う Linux プラットフォーム (最重要な例としては Alpine Linux) 向けのものです。文法としては musllinux_x_y_arch
で、 musl x.y
およびそれ以降で arch
なるアーキテクチャをサポートします。
musl のバージョンの値は、 Python インタープリタが現在その上で走行している musl libc 共有ライブラリを実行し、その出力を字句解析することで得られます:
import re
import subprocess
def get_musl_major_minor(so: str) -> tuple[int, int] | None:
"""Detect musl runtime version.
Returns a two-tuple ``(major, minor)`` that indicates musl
library's version, or ``None`` if the given libc .so does not
output expected information.
The libc library should output something like this to stderr::
musl libc (x86_64)
Version 1.2.2
Dynamic Program Loader
"""
proc = subprocess.run([so], stderr=subprocess.PIPE, text=True)
lines = (line.strip() for line in proc.stderr.splitlines())
lines = [line for line in lines if line]
if len(lines) < 2 or lines[0][:4] != "musl":
return None
match = re.match(r"Version (\d+)\.(\d+)", lines[1])
if match:
return (int(match.group(1)), int(match.group(2)))
return None
Python インタープリタがその上で走行している musl ライブラリの場所を見つけるためには、現在、二つの可能なやり方があり、ひとつはシステムの ldd コマンドによるもの、もうひとつは 実行ファイルの ELF ヘッダから PT_INTERP
セクションの値をパースする方法です。
使い方#
タグ類は、 (もしダウンロードが必要なら) 選択可能なビルド済配布物のリストの中からどれをダウンロードするのかをインストーラが決定するために使われます。インストーラは、自身がサポートする (pyver, abi, arch) タプルのリストを維持管理しています。ビルド済配布物のタグがリストに含まれて (in
) いれば、それをインストールすることができます。
古い Python リリース向けに発行された純 Python のバージョンにフォールバックするよりも前に、利用できる中で最も機能を網羅したビルド済配布物 (インストールする先の環境に最も適したもの) をインストーラがデフォルトで選択することが推奨されています。インストーラは、また、受け入れ可能な互換性タグのリストを設定変更したり順序を入れ替えたりする方法を持つことが推奨されています; 例えば、ユーザは、純 Python であると広報しているビルド済みパッケージだけをダウンロードするために、 *-none-any
タグだけを受容しても構いません。
互換性はあるがもはや古くなってしまったビルド済みのものを使う選択肢よりもより好ましいという点で、インストーラに望まれるもうひとつの機能は "可能ならソースコードから再コンパイルする" でしょう。
この事例集は、インストーラを linux_x86_64 システム上の CPython 3.3 のもとで走らせるためのものです。最も好ましいもの (最新版の Python 向けにビルドされたコンパイル済みの拡張モジュールが付属している配布物) から、最も好ましくはないもの (古いバージョンの Python でビルドされた純 Python の配布物) へ、という順序で並んでいます:
cp33-cp33m-linux_x86_64
cp33-abi3-linux_x86_64
cp3-abi3-linux_x86_64
cp33-none-linux_x86_64*
cp3-none-linux_x86_64*
py33-none-linux_x86_64*
py3-none-linux_x86_64*
cp33-none-any
cp3-none-any
py33-none-any
py3-none-any
py32-none-any
py31-none-any
py30-none-any
ビルド済み配布物は、サブプロセスとして起動されるネーティブの実行可能ファイルを含んでいるなどのようなC 言語拡張以外の理由によって、特定のプラットフォーム向けであっても構いません。
時々、あるパッケージの特定のバージョンとして複数のビルド済み配布物が存在することがあるでしょう。例えば、パッケージ製作者が、 cp33-abi3-linux_x86_64
というタグを付けて追加的な C 言語拡張を含むパッケージをリリースし、そのようなものを含まない同じ配布物に py3-none-any
というタグを付けてリリースするような場合です。(このような場合でも) サポートされるタグのリストで先に出現する方を優先するという理由によって C 言語拡張付きのパッケージがそうでないパッケージよりも選好されてインストールされるという形で、インデックスによってどちらかに決めることができます。
圧縮されたタグのセット#
bdists のコンパクトなファイル名で、互換性のあるタグトリプルが複数ある場合にもきちんと動作するものを許容するためには、代わりにファイル名の中のそれぞれのタグが '.' で分割可能でありソート可能であるような一連のタグになっていることが可能です。例えば、 pip は純 Python のパッケージで、同一のソースコードで Python 2 でも 3 でも動作するように書かれていますが、これは py2.py3-none-any
というタグを付けた bdist として配布することができるでしょう。単純なタグの完全なリストは:
for x in pytag.split('.'):
for y in abitag.split('.'):
for z in platformtag.split('.'):
yield '-'.join((x, y, z))
このスキームを実装する bdist フォーマットは、拡張されたタグ群を bdist に特有のメタデータの中に含んでいるべきです。この圧縮スキームは、サポートされていないタグや例えば "cp33-cp31u-win64" のようにいかなる Python 実装においてもサポートされていない "不可能な" タグを大量に生成すると思われるので、控えめに使うようにしてください。
FAQ#
- デフォルトではどんなタグが使われますか?
ツール類は、
cp33-cp33m-win32
のようなアーキテクチャへの依存を示すタグやpy33-none-any
のような純 Python タグの中で最も好ましいものをデフォルトで採用するべきです。パッケージ製作者がデフォルトをオーバーライドしていたとすれば、それは彼らが異なる Python 間での互換性を提供しようという意図があったことを示しています。- 自分の配布物が最新版の Python と相容れない機能を使っているとしたら、どんなタグを使いますか?
互換性タグは、インストーラがある配布物の 単一のバージョン の 最も互換性がある ビルドを選択する際に助けとなります。例えば、 (Python 3.4 に特有の機能を使っている)
beaglevote-1.2.0``には Python 3.3 と互換性を持つビルドがひとつもないという場合でも、 ``py34-none-any
タグの代わりにpy3-none-any
タグを使っても構いません。 Python 3.3 のユーザが互換性のあるビルドを得るためには、新しい機能を使う前のリリースであるbeaglevote-1.1.0
用の要求 (requirement) などを他の指定子を組み合わせなければなりません。- Python のバージョン番号に
.
がないのはなぜですか? CPython は、数字3個のメジャーリリースなしで 20 年以上にわたって存続してきました。これはしばらくの間は続くに違いありません。 - や . が周辺のファイル名を区切る役割を果たしているので、他の実装では _ を区切り子として使っても構いません。
- ハイフンやその他の英数字以外の文字をアンダースコアに正規化するのはなぜですか?
ファイル名の部分部分を区分けする
.
文字や-
文字との干渉を避けるために、かつ、 (クォートすることなく URL パス内で使用可能であることを含む) 数多あるファイルシステムのファイル名に対する制限事項との間のより良い互換性のために。- 何故、
.
や-
の代わりに特殊な文字 <X> を使わないのですか? それは、その文字が不便であるかコンテクストによっては潜在的に混乱を招きやすい (例えば
+
は URL 内ではクォートしなければなりませんし、~
は POSIX でユーザのホームディレクトリを示すために使われます) から、あるいは、 PEP 427 で定義された wheel フォーマットを参照して作成された既存の参照実装を変更すること (例えば、圧縮タグで部分部分を分割するのに.
ではなく,
を使うようにすること) を正当化するに足りるだけのアドバンテージがないから、のいずれかです。- 誰が実装に関する短縮形のレジストリの維持管理をしているのですか?
python-dev メーリングリストで要求することで、新しい2文字省略形の割り当てを受けることができるでしょう。経験上は、その時点で最も卓越した4個の実装のために省略形が予約されています。
- 互換性タグは METADATA に含まれるのか、あるいは PKG-INFO か?
否。互換性タグはビルド済み配布物のメタデータの一部です。METADATA / PKG-INFO は、その配布物の単一のビルドではなく配布物の全体にとって正当なものであるべきです。
- どうして私のお気に入りの Python 実装について言及しなかったの?
省略形タグは、コンパイル済みの Python コードを公開のインデックスでシェアすることを促進します。あなたの Python 実装においてもこの仕様を使うことができますが、しかしもっと長いタグになってしまうことでしょう。すべての "純 Python" なビルド済み配布物が単に
py
を使うだけであることを思い出してください。- どうして参照実装における ABI タグ (第2のタグ) は時々 "none" なのですか?
Python 2 では SOABI (より新しい版の Python 3 から来た概念) を作成する簡単な方法がないので、本稿執筆時点の参照実装は "none" なのです。理想的には、それはもっと新しい版の Python に相似の "py27(d|m|u)" を検出するようになるでしょうが、それまでの間は "知られていない" ことを示すのに "none" とすることが必要十分な方法なのです。