web-dev-qa-db-ja.com

Python Python3の名前空間パッケージ

名前空間パッケージのトピックは、初心者にとっては少し混乱しているように見えます。以前のバージョンのPythonがいくつかの異なる方法で実装したことや、StackOverflowに関する多くのQ&Aがそれを実装していることは役に立ちません。 _Python 3.5_以降で解決策を探しています。

シナリオ:

たくさんのPythonコードをモジュールとサブモジュールにリファクタリングし、同じ名前空間に座っている間、これらの各プロジェクトが互いに独立して動作するように設定する作業を行っています。

最終的には、内部PyPiサーバーを使用して、これらのパッケージを内部ネットワークに提供し、外部(パブリック)PyPiパッケージと混同しないようにします。

例: 2つのモジュールがあり、次のことを実行できるようにしたいと思います。

_from org.client.client1 import mod1
from org.common import config
_

反映されたモジュールは、次のように分離されます。

リポジトリ1:

_org_client_client1_mod1/
  setup.py
  mod1/
    __init__.py
    somefile.py
_

リポジトリ2:

_org_common_config/
  setup.py
  config/
    __init__.py
    someotherfile.py
_

私のGitリポジトリはすでに_org_client_client1_mod1_と_org_common_config_としてセットアップされているので、パッケージと___init__.py_ファイルでセットアップを実行する必要があると思います。

質問:

#1

___init__.py_の場合、(もしあれば)これらのどれを使用する必要がありますか?:

_from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
_

または:

_import pkg_resources
pkg_resources.declare_namespace(__name__)
_

#2

_setup.py_を使用する場合でも、_namespace_modules_パラメーターを追加する必要がありますか?追加する場合は、_namespace_modules=['org.common']_または_namespace_modules=['org', 'common']_?

#3

どういうわけかこれを別の方法で実装するだけで、上記のすべてを放棄できますか?おそらくもっと単純な、またはより「Pythonic」なものですか?

28
Bobby

パーティーに遅れましたが、Pythonで名前空間のパスを下る仲間の旅行者を助けるために傷つくことはありません!

#1:

init。pyで、これらのどれを使用する必要がありますか(もしあれば)?:

リストされているように名前空間パッケージを実行するには3つの方法があります ここ

  1. ネイティブ名前空間パッケージを使用します。このタイプの名前空間パッケージはPEP420で定義されており、Python 3.3以降で使用できます。名前空間内のパッケージがPython = 3およびpipを介したインストール。

  2. Pkgutilスタイルの名前空間パッケージを使用します。これは、Python 2と3、およびpipとpython setup.pyinstall)の両方を介したインストールをサポートする必要がある新しいパッケージに推奨されます。

  3. Pkg_resourcesスタイルの名前空間パッケージを使用します。この方法をすでに使用しているパッケージとの互換性が必要な場合、またはパッケージをZipセーフにする必要がある場合は、この方法をお勧めします。

#2(_pkgutil-style_)または#3(_pkg_resources-style_)を使用している場合は、___init__.py_ファイルに対応するスタイルを使用する必要があります。ネイティブ名前空間を使用する場合、名前空間ディレクトリに___init__.py_はありません。

#2:

Setup.pyを使用して、namespace_modulesパラメーターを追加する必要がありますか?追加する場合は、namespace_modules = ['org.common']、またはnamespace_modules = ['org'、 'common']を使用しますか?

名前空間パッケージの選択がネイティブスタイルでない場合は、はい、setup()に_namespace_packages_が必要になります。

#3:

どういうわけかこれを別の方法で実装するだけで、上記のすべてを放棄できますか?おそらくもっと単純な、またはもっと「Pythonic」なものですか?

最終的にPythonで完全なトピックにたどり着いたので、何をしているのか、何をしたいのかを知っているようで、Python名前空間パッケージを作成することがそれを行う方法であることがわかりました。これは、問題を解決するためのPythonの方法と見なされます。


あなたの質問に加えて、ここに私が発見したいくつかのことがあります:

PEP42Pythonパッケージガイド を読み、名前空間パッケージの理解に多くの時間を費やしました。一般的に、それがどのように機能するかを理解しました。私はいくつかの答えを読みました ここここここ 、そしてこの答えはStackOverflowでも同様です。こことGitの例 link Robが共有しています。

しかし、私の問題は、パッケージを作成した後でした。すべての命令とサンプルコードがsetuptools.setup(package=[])関数にパッケージを明示的にリストしているため、私のコードは失敗しました。私のサブパッケージ/ディレクトリは含まれていませんでした。深く掘り下げてみると、setuptoolsにはサブパッケージの追加にも役立つfind_namespace_package()関数があることがわかりました。

[〜#〜]編集[〜#〜]

find_namespace_packages()へのリンク(setuptoolsバージョンが_40.1.0_より大きい): https://setuptools.readthedocs.io/en/latest/setuptools.html#find-名前空間-パッケージ

編集(2019年8月9日):

答えを完成させるために、例を挙げて再構築します。

次の解決策は、暗黙の名前空間パッケージをサポートするPython 3.3+)を想定しています

Pythonバージョン_3.5_以降のソリューションを探しているので、提供されているコードサンプルを取得してさらに詳しく説明しましょう。

次のことを想定しましょう。

名前空間/ Pythonパッケージ名:org

配布パッケージ:_org_client_、_org_common_

Python:_3.3+_

setuptools:_40.1.0_

あなたがするために

_from org.client.client1 import mod1
from org.common import config
_

そして、トップレベルのディレクトリを同じに保ちます。 _org_client_client1_mod1_および_org_common_config_、構造を次のように変更できます

リポジトリ1:

_org_client_client1_mod1/
  setup.py
  org/
    client/
      client1/
        __init__.py
        submod1/
          __init__.py
        mod1/
          __init__.py
          somefile.py
        file1.py
_

更新された_setup.py_

_from setuptools import find_namespace_packages, setup
setup(
    name="org_client",
    ...
    packages=find_namespace_packages(), # Follows similar lookup as find_packages()
    ...
)
_

リポジトリ2:

_org_common_config/
  setup.py
  org/
    common/
      __init__.py
      config/
        __init__.py
        someotherfile.py
_

更新された_setup.py_:

_from setuptools import find_namespace_packages, setup
setup(
    name="org_common",
    ...
    packages=find_namespace_packages(), # Follows similar lookup as find_packages()
    ...
)
_

インストールするには(pipを使用):

_(venv) $ pip3 install org_common_config/
(venv) $ pip3 install org_client_client1_mod1/
_

更新されたpipリストには次のように表示されます。

_(venv) $ pip3 list
...
org_client
org_common
...
_

ただし、インポートすることはできません。インポートするには、_org.client_および_org.common_表記に従う必要があります。

理由を理解するには、ここを参照できます(venv内を想定)。

_(venv) $ cd venv/lib/python3.5/site-packages/
(venv) $ ls -l | grep org
_

_org_client_または_org_common_ディレクトリがなく、名前空間パッケージとして解釈されていることがわかります。

_(venv) $ cd venv/lib/python3.5/site-packages/org/
(venv) $ ls -l
client/
common/
...
_
5
rite2hhh

これは難しい問題です。どこにでもあるすべての-_、および__init__.pyは、私たちにとって必ずしも簡単ではありません。

まず、私はあなたの質問に答えます:

__init__.pyで、(もしあれば)これらのどれを使うべきですか?

  • __init__.pyは完全に空にすることができ、正しい場所に配置する必要があります。つまり、(しゃれ)pythonコード(setup.pyを除く)を含むサブパッケージに含まれている必要があります。)これらのルールに従うと、問題ないはずです。

setup.pyを使用する場合でも、namespace_modulesパラメーターを追加する必要がありますか?追加する場合は、namespace_modules=['org.common']またはnamespace_modules=['org', 'common']を使用しますか?

  • いいえ! name=packages=のみ。ただし、ディレクトリ構造と比較したpackages=引数の形式に注意してください。
  • package=引数の形式は次のとおりです。 setup.py for namespace package
  • 対応するディレクトリ構造は次のとおりです。 example_package/a/<subpackage>

これをなんらかの方法で実装するだけで、上記のすべてを放棄できますか?おそらくもっと単純な、またはもっと「Pythonic」なものですか?

  • 複数の機能を個別にインストールできるようにしたいが、同じトップレベルの名前空間の下にある場合は、正しい方向に進んでいます。

この回答の残りの部分では、名前空間パッケージをネイティブ形式で再実装します。

見つけたすべての役立つドキュメントを投稿の下部に配置します。

Kなので、ネイティブの名前空間パッケージが必要だと仮定します。まず、2つのリポジトリの現在の構造を見てみましょう。

org_client_client1_mod1/
  setup.py
  mod1/
    __init__.py
    somefile.py

org_common_config/
  setup.py
  config/
    __init__.py
    someotherfile.py

これは簡単すぎるでしょう!!!

欲しいものを手に入れるには:

私の脳は、名前空間パッケージで3レベル深くなることができるかどうかを知るのに十分な弾力性がありませんが、あなたがやりたいことをするために、あなたがやりたいと確信していることは次のとおりです。

org-client/
  setup.py
  org/
    client/
      client1/
        __init__.py
        mod1/
          __init__.py
          somefile.py

org-common-but-also-note-this-name-doesnt-matter/
  setup.py
  org/
    common/
      __init__.py
      config/
        __init__.py
        someotherfile.py

基本的に、キーは、各name=内のstuptools.setup()に正しいpackages=setup.py引数を指定することです。

これらは次のようになります。

name='org_client',
...
packages=['org.client']

name='org_common'
...
packages['org.common']

それぞれ。

次に、各トップレベルディレクトリ内にpip install .を付けてそれぞれをインストールします。

最初のモジュールをインストールすると、somefile.pyモジュールにアクセスでき、2番目のモジュールをインストールすると、someotherfile.pyにアクセスできます。また、同じ環境にorgという名前の2つのパッケージをインストールしようとしても混乱することはありません。

Kなので、ドキュメントの最も役立つセクション: https://packaging.python.org/guides/packaging-namespace-packages/#packaging-namespace-packages

そして、これが私が実際にこれを理解するようになった方法です: https://github.com/pypa/sample-namespace-packages/tree/master/native

2
Rob Truxal