パッケージフォーマット#

このページでは、 Python パッケージの配布に使われるファイルフォーマット群とその相違点について議論します。

PyPI のようなパッケージインデックス上には、2種類のフォーマットが見つかることでしょう: ソースコード配布物 <source distributions> 、短縮形で sdists**と呼ばれるものと **バイナリ配布物 <binary distributions> 、普通は wheels を呼ばれるものです。例えば、 pip 23.3.1 用の PyPI ページ では、 pip-23.3.1.tar.gzpip-23.3.1-py3-none-any.whl の二つのファイルをダウンロードすることができます。前者は sdist であり、後者は wheel です。下で説明するように、これらは異なった目的のために提供されています。 PyPI (にせよ他のどこかにせよ) パッケージを公開する時は、常に sdist とひとつまたは複数の wheel の両方をアップロードするべきです。

ソースコード配布物とは何か?#

概念としては、ソースコード配布物は生の形のソースコードをアーカイブしたものです。具体的に言うと、sdist は、ソースコードに加えて、プロジェクトのメタデータを保持する PKG-INFO と言う名の特別なファイルをアーカイブした .tar.gz ファイルです。このファイルの存在によってメタデータを自分自身で計算しなくても済むようにすることでパッケージングツール群を補助しています。 PKG-INFO ファイルは、 コアとなるメタデータ で指定されたフォーマットに従うもので、人の手で書くことが想定されていないものです [1]

従って、tar アーカイブを扱える標準的なツール、例えば UNIX プラットフォーム (Linux や macOS) 上の tar -xvf や任意のプラットフォーム上の Python の tarfile モジュールのコマンドラインインターフェース を使ってアンパックすることで、ある sdist の内容を検査することができます。

sdist は、パッケージングのエコシステムの中でいくつかの目的のために使用されます。Python パッケージインストーラの標準である pip がインストールするべき wheel を見つけられない時にソースコード配布物をダウンロードしてそこから wheel をコンパイルし、その wheel をインストールするようにフォールバックすることでしょう。さらに、さまざまな理由で例えば Git リポジトリからプルすることなどよりも (sdist を) 好むダウンストリームパッケージ (Linux ディストリビューション、 Conda 、 macOS 上の Homebrew や MacPorts など ...)にとってのパッケージソースとしてしばしば sdist が使われます。

ソースコード配布物は、 package_name-version.tar.gz の形、例えば pip-23.3.1.tar.gz のファイル名で識別されます。

sdist 形式の技術的な詳細を知りたければ、 sdist 仕様 を見てください。

wheel とは何か?#

概念としては、 wheel は、パッケージをインストールする際にコピーされる必要のあるファイル群をそのまま含んでいるものです。

プラットフォームに依存した機械語にコンパイルする必要がある C や C++ や Rust のようなコンパイル言語で書かれた 拡張モジュール を伴うパッケージにおいては、sdist と wheel の間には大きな違いがあります。このようなパッケージでは、 wheel は (C言語のソースのような) ソースコードを内包しておらず、代わりに (Linux における .so ファイルや Windows における DLL のような) コンパイル済みで実行可能なコードを含んでいます。

さらにまた、あるバージョンのプロジェクトには sdist がひとつしか存在しない一方で、wheel はたくさんあるかもしれません。繰り返しになりますが、これは拡張モジュールの文脈でもっとも有りがちなことです。拡張モジュールのコンパイル済みのコードはオペレーティングシステムとプロセッサーアーキテクチャに、また、 (Python stable ABI を使っていなければ) しばしば Python インタープリタのバージョンに紐付いています。

純 Python のパッケージでは、 sdist と wheel の差異はあまり顕著ではありません。普通は、あらゆるプラットフォームと Python のバージョン向けに wheel がたったひとつ存在します。 Python はインタープリタ言語であり、事前にコンパイルしておく必要がなく、それゆえに wheel は sdist と同様に .py ファイル群を内包しているのです。

.pyc バイトコードファイル群: これらは wheel には含まれていませんが、これらについて不思議に思っているなら、これらは安価に生成することができ、また、もしこれをパッケージに含めるとすれば、単一の wheel を配布する代わりに、Python のバージョン毎に巨大な数のパッケージを配布するという不必要なことを強いられることになるでしょう。代わりに、 pip のようなインストーラがインストール中に生成しています。

そうは言っても、たとえ純 Python のプロジェクトであったとしても、 sdist と wheel の間には依然として重要な違いがあります。 wheel は、インストールされるものだけを正確に含むように、それ以外のものは含まないようにと意図して作られています。とりわけ、 wheel にはテストや説明文書を決して含めるべきではありませんが、他方で sdist では普通はこれらを含みます。また、 wheel のフォーマットは sdist のものよりもより複雑です。例えば、wheel にはある特別なファイル -- RECORD と呼ばれます -- が含まれていて、このファイルには wheel 内の他のすべてのファイルのハッシュ値付きのリストが含まれており、安全性の確認やダウンロードの際の完全性の確認に使われます。

一見したところでは、 wheel こそが "単純かつ基本的な" 純 Python プロジェクトに必要なものではないかと思うかもしれません。 sdist の自由度が大きいために pip のようなインストーラは sdist から直接にインストールすることができないということを覚えておいてください -- まず sdist が指定する ビルドバックエンド を起動することで (ビルドバックエンドはビルドして wheel を作成している間に C 言語拡張のコンパイルのようなあらゆる種類の変形をすべて行います) wheel をビルドする必要があるのです。こう言う理由で、たとえ純 Python のプロジェクトであっても、PyPI やその他のパッケージインデックスには常に sdist と wheel の 両方 をアップロードしておくべきなのです。こうすることで、wheel なら直接にインストールできるので、ユーザにとってはインストールがとても素早く行えることになります。インストールされるファイルだけを含むという wheel の性質は、ダウンロードのサイズが小さくなることにもつながります。

技術的なレベルでは、 wheel は (sdist が TAR アーカイブであるのとは違って) ZIP アーカイブです。例えば Linux や macOS のような UNIX 系プラットフォームなら unzip 、 Windows 上の PowerShell 内なら Expand-Archive 、あるいは Python の zipfile モジュールのコマンドライン <python:zipfile-commandline>` を使って通常の ZIP アーカイブとして展開することで、内容を検査することができます。これは、 wheel ファイルが必要なすべてのファイルを内包していることを確認するのにとても便利です。

wheel の中には、パッケージのファイル群と package_name-version.dist-info と呼ばれる追加のディレクトリがあります。このディレクトリには、 sdist における PKG-INFO に相当する METADATA ファイルや RECORD などのさまざまなファイルを含みます。これは、wheel 内のファイルがひとつも欠けていないことを保証するのに役立ちます。

wheel のファイル名は (ほとんど使われることのない幾つかの機能を除けば) このように見えます: package_name-version-python_tag-abi_tag-platform_tag.whl 。この命名規則によって、その wheel がどのプラットフォームでどの Python バージョンと互換性を持つのかを識別することができます。例えば、 pip-23.3.1-py3-none-any.whl という名前は次のことを意味します:

  • (py3) CPython、最も広く使われている Python 実装、あるいは PyPy のような代替実装でも、 Python 3 の実装があればどこでも、この wheel をインストールすることができます;

  • (none) Python のバージョンには依存しません;

  • (any) プラットフォームには依存しません。

py3-none-any というパターンは、純 Python のプロジェクトに広く見られます。拡張モジュールを伴うパッケージは、典型的に、もっと複雑なタグ群を持つ複数の wheel を出荷します。

wheel のフォーマットに関するすべての技術的な詳細は、 wheel 仕様 で見つかるでしょう。

egg って何?#

"Egg" は、 wheel によって置き換えられた古いパッケージフォーマットです。もはや使用されるべきではありません。 2023 年 8 月以降、 PyPI は egg のアップロードを拒絶 しています。

wheelと egg の重要な差異について以下にまとめます。

  • egg のフォーマットは、2004年に:ref:setuptools`によって、また、wheel のフォーマットは2012年に :pep:`427 によって導入されました。

  • Wheel には 公式の標準仕様 が存在します。 Egg にはありません。

  • Wheel は :term:`配布物 <Distribution Package>`のフォーマット、つまり、パッケージングのフォーマットです [2] 。 Egg は配布物のフォーマットでもあり、かつ、(もし圧縮されたままであれば)実行時のインストールフォーマットであって、 importができるように設計されています。

  • Wheel 形式のファイルには .pyc ファイルが含まれていません。従って、Pythonファイルだけが配布物に含まれている (即ちコンパイル済みの拡張がない) 場合、かつ、 Python 2 および 3 と互換性を持つ時、その wheel ファイルは、 sdist と同様に "汎用 <universal>" である可能性があります。

  • Wheel は、.dist-info ディレクトリ を使います。 Egg は .egg-info を使います。

  • Wheel には :ref:`多彩なファイル命名慣行 <wheel-file-name-spec>`が存在します。単独のwheelアーカイブはPython言語のバージョンや実装、ABI、そしてシステムのアーキテクチャとの互換性を表示することができます。

  • Wheel はバージョン付けされています。それぞれのwheelファイルは、wheelの仕様のバージョンやパッケージングに使われた実装のバージョンを保持しています。

  • wheel は、内部では、 sysconfigパスの型 に従って整理されているので、他のフォーマットに変換するのがより簡単になっています。