名前空間パッケージをパッケージする#

名前空間 (Namespace) パッケージは、複数かつ個別の パッケージ (曖昧にならないようにこの文書では 配布物 と呼ぶことにする) を越えて、単一の パッケージ の中のサブパッケージやモジュールを分離できるようにすることができます。例えば、下に示すようなパッケージ構造であれば:

mynamespace/
    __init__.py
    subpackage_a/
        __init__.py
        ...
    subpackage_b/
        __init__.py
        ...
    module_b.py
pyproject.toml

そして、このパッケージを自分のソースコード中で使うにはこのようにする:

from mynamespace import subpackage_a
from mynamespace import subpackage_b

そうすることで、これらのサブパッケージ群を別々のふたつの配布物に分割することができます:

mynamespace-subpackage-a/
    pyproject.toml
    src/
        mynamespace/
            subpackage_a/
                __init__.py

mynamespace-subpackage-b/
    pyproject.toml
    src/
        mynamespace/
            subpackage_b/
                __init__.py
            module_b.py

それぞれのサブパッケージは、今や、個別にインストール・使用・バージョン管理することができます。

名前空間パッケージは、 (単独の会社から出ている複数の製品向けのクライアントライブラリの巨大な集積のような) 緩やかに関連したパッケージが多数含まれるコレクションに役立つでしょう。しかしながら、名前空間パッケージにはいくつかの注意書きがあって、全ての場合に適切と言うわけではありません。単純明快な代替策は、あなたの配布物のすべてについて import mynamespace_subpackage_a のような接頭語を使うことです (インポートするオブジェクトの名前を短く保つために import mynamespace_subpackage_a as subpackage_a のようにインポートすることさえできます) 。

名前空間パッケージを作成する#

現在、名前空間パッケージを作るには2個の異なる手法がありますが、後者は使わない方が良いとされています:

  1. 組み込みの名前空間パッケージ を用いる方法。この種の名前空間パッケージは PEP 420 で定義されていて、 Python 3.3 およびそれ以降で利用することができます。パッケージ中の名前空間が Python 3 だけをサポートすればよくて pip でインストールするのであれば、これが推奨される方法です。

  2. レガシー名前空間パッケージ群 を使ってください。これは`pkgutil 型の名前空間パッケージ`_ および pkg_resources 型の名前空間パッケージ から構成されています。

組み込みの名前空間パッケージ#

Python 3.3 では PEP 420 から 暗黙の 名前空間パッケージを追加しました。ネイティブな名前空間パッケージを作成するのに必要なことは、名前空間パッケージのディレクトリから __init__.py を取り除くことだけです。ファイル構造の例はこちら (ソースコードレイアウト に従います):

mynamespace-subpackage-a/
    pyproject.toml # AND/OR setup.py, setup.cfg
    src/
        mynamespace/ # namespace package
            # No __init__.py here.
            subpackage_a/
                # Regular import packages have an __init__.py.
                __init__.py
                module.py

名前空間パッケージを用いる各配布物で __init__.py を省くこと、または、 pkgutil 型の __init__.py を使用することが極めて重要です。もしいずれかの配布物でこれを忘れると、名前空間の論理が破綻して、他のサブパッケージをインポートすることができなくなります。

src-layout ディレクトリ構造によって、ほとんどの ビルドバックエンド が自動的にパッケージ群を発見できるようになります。もっと情報が欲しい場合は src レイアウト対フラットレイアウト を見てください。しかしながら、パッケージの包含・除外を自分自身で管理したいのであれば、トップレベルの pyproject.toml を設定することで可能です:

[build-system]
...

[tool.setuptools.packages.find]
where = ["src/"]
include = ["mynamespace.subpackage_a"]

[project]
name = "mynamespace-subpackage-a"
...

同じことが setup.cfg で達成できます:

[options]
package_dir =
    =src
packages = find_namespace:

[options.packages.find]
where = src

または setup.py ファイル:

from setuptools import setup, find_namespace_packages

setup(
    name='mynamespace-subpackage-a',
    ...
    packages=find_namespace_packages(where='src/', include=['mynamespace.subpackage_a']),
    package_dir={'': 'src'},
)

Setuptools は、デフォルトでは暗黙の名前空間パッケージを探してディレクトリ構造を探索します。

ふたつの名前空間パッケージの完全な動作例は、 組み込みの名前空間パッケージの使用例プロジェクト にあります。

注釈

組み込み名前空間パッケージと pkgutil 型の名前空間パッケージは、多くの部分で互換性があるので、 Python 3 しかサポートしない配布物では組み込み名前空間パッケージを使用し、 Python 2 と Python 3 の両方をサポートしなければならない配布物では pkgutil型名前空間パッケージを使うことが可能です。

伝統的な名前空間パッケージ群#

これらのふたつの方法は、 PEP 420 以前に名前空間を作成するのに使われましたが、今では過去のやり方であると考えられていて、すでにこの方法を使っているパッケージとの互換性を必要とする場合を除いては使われるべきではないと考えられています。また、 pkg_resources は非推奨になりました。

既存のパッケージを移植するためには、名前空間を共有するすべてのパッケージが同時に移植されなければなりません。

警告

組み込みの名前空間パッケージや pkgutil 型の名前空間パッケージは多くの点で互換性がありますが、 pkg_resources 型の名前空間パッケージは他の手法とは互換性がありません。同じ名前空間向けにパッケージを提供するような複数の配布物で、異なる手法を用いることは推奨されません。

pkgutil 型名前空間パッケージ#

Python 2.3 で pkgutil モジュールと pkgutil.extend_path() 関数が導入されました。Python 2.3+ と Python 3 の両方に互換性を持つ必要がある名前空間パッケージを宣言するのにこれが使えるかもしれません。これは、互換性のレベルが最も高くなるアプローチとして推奨されています。

pkgutil 型の名前空間パッケージを作成するには、その名前空間パッケージ用に __init__.py ファイルを準備する必要があります:

mynamespace-subpackage-a/
    src/
        pyproject.toml # AND/OR setup.cfg, setup.py
        mynamespace/
            __init__.py  # Namespace package __init__.py
            subpackage_a/
                __init__.py  # Regular package __init__.py
                module.py

名前空間パッケージ用の __init__.py ファイルは、次に示すものを含んでいる必要があります:

__path__ = __import__('pkgutil').extend_path(__path__, __name__)

ある名前空間パッケージを用いる すべての 配布物は、 __init__.py を持っていなければなりません。もしいずれかの配布物でそうなっていなければ、名前空間の論理破綻を招き、他のサブパッケージをインポートすることができなくなるでしょう。 __init__.py に他のコードを追加しても、それはアクセスできないものとなるでしょう。

pkgutil 型の名前空間パッケージのふたつの動作例が pkgutil 型名前空間を例示するプロジェクト にあります。

pkg_resources 型名前空間パッケージ#

Setuptools は、 pkg_resources.declare_namespace 関数と setup() に渡す namespace_packages 引数を提供します。これらを一緒に使うことで名前空間パッケージを宣言することができます。この手法はもはや推奨されていませんが、既存の名前空間パッケージのほとんどで使われています。この手法を採用している既存の名前空間パッケージの中に新しい配布物を作成する時には、異なる手法が相互に互換ではないために既存パッケージを移植しようとすることが推奨されていないので、この手法を採用し続けることを推奨します。

pkg_resources 型名前空間パッケージを作成するには、名前空間パッケージ用の __init__.py を準備する必要があります:

mynamespace-subpackage-a/
    src/
        pyproject.toml # AND/OR setup.cfg, setup.py
        mynamespace/
            __init__.py  # Namespace package __init__.py
            subpackage_a/
                __init__.py  # Regular package __init__.py
                module.py

名前空間パッケージ用の __init__.py ファイルは、次に示すものを含んでいる必要があります:

__import__('pkg_resources').declare_namespace(__name__)

ある名前空間パッケージを用いる すべての 配布物は、 __init__.py を持っていなければなりません。もしいずれかの配布物でそうなっていなければ、名前空間の論理破綻を招き、他のサブパッケージをインポートすることができなくなるでしょう。 __init__.py に他のコードを追加しても、それはアクセスできないものとなるでしょう。

注釈

いくつかの古めの推奨では、次のような名前空間パッケージ用 __init__.py を使うように言っています:

try:
    __import__('pkg_resources').declare_namespace(__name__)
except ImportError:
    __path__ = __import__('pkgutil').extend_path(__path__, __name__)

背景にあるアイデアとしては、 setuptools を使えないような稀な場合には、パッケージを pkgutil 型パッケージに切り戻したいと言うことがあるかもしれません。 pkgutil 型と pkg_resources 型の名前空間パッケージは互いに非互換なので、これは推奨できるものではありません。 setuptool が存在するか否かが問題なのであれば、パッケージとしては install_requires を通じて setuptools に明示的に依存すると示しておくべきです。

最後に、それぞれの配布物は setup.pysetup() 向けに namespace_packages 引数を準備しておく必要があります。例えば:

from setuptools import find_packages, setup

setup(
    name='mynamespace-subpackage-a',
    ...
    packages=find_packages()
    namespace_packages=['mynamespace']
)

pkg_resources 型の名前空間パッケージの動作可能な二つの例が pkg_resources 型名前空間例示プロジェクト で見つかるはずです。