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

このページでは、 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 内のファイルがひとつも欠けていないことを保証するのに役立ちます。

The file name of a wheel (ignoring some rarely used features) looks like this: package_name-version-python_tag-abi_tag-platform_tag.whl. This naming convention identifies which platforms and Python versions the wheel is compatible with. For example, the name pip-23.3.1-py3-none-any.whl means that:

  • (py3) This wheel can be installed on any implementation of Python 3, whether CPython, the most widely used Python implementation, or an alternative implementation like PyPy;

  • (none) It does not depend on the Python version;

  • (any) It does not depend on the platform.

The pattern py3-none-any is common for pure Python projects. Packages with extension modules typically ship multiple wheels with more complex tags.

All technical details on the wheel format can be found in the wheel specification.

What about eggs?#

"Egg" is an old package format that has been replaced with the wheel format. It should not be used anymore. Since August 2023, PyPI rejects egg uploads.

Here's a breakdown of the important differences between wheel and egg.

  • The egg format was introduced by Setuptools in 2004, whereas the wheel format was introduced by PEP 427 in 2012.

  • Wheel has an official standard specification. Egg did not.

  • Wheel is a distribution format, i.e a packaging format. [2] Egg was both a distribution format and a runtime installation format (if left zipped), and was designed to be importable.

  • Wheel archives do not include .pyc files. Therefore, when the distribution only contains Python files (i.e. no compiled extensions), and is compatible with Python 2 and 3, it's possible for a wheel to be "universal", similar to an sdist.

  • Wheel uses standard .dist-info directories. Egg used .egg-info.

  • Wheel has a richer file naming convention. A single wheel archive can indicate its compatibility with a number of Python language versions and implementations, ABIs, and system architectures.

  • Wheel is versioned. Every wheel file contains the version of the wheel specification and the implementation that packaged it.

  • Wheel is internally organized by sysconfig path type, therefore making it easier to convert to other formats.