"importlib.metadata" -- パッケージメタデータへのアクセス
********************************************************

Added in version 3.8.

バージョン 3.10 で変更: "importlib.metadata" は暫定的なものではなくな
りました。

**ソースコード:** Lib/importlib/metadata/__init__.py

"importlib.metadata" はインストールされた 配布パッケージ のエントリポ
イントやトップレベル名 (あれば、 パッケージ やモジュール) のようなメタ
データへのアクセスを提供するライブラリです。Pythonのインポートシステム
をベースに構築されており、このライブラリは "pkg_resources" の entry
point API と metadata API にある同様の機能を置き換えることを目的として
います。"importlib.resources" と共に、このパッケージは古くて効率の悪い
"pkg_resources" パッケージを使う必要性を無くすことができます。

"importlib.metadata" は、Pythonの "site-packages" ディレクトリに pip
などのツールでインストールしたサードパーティの *配布パッケージ* に対し
て動作します。具体的には、 "dist-info" や "egg-info" ディレクトリを持
つ配布物や、 コアとなるメタデータの仕様 で定義されたメタデータを検出で
きるようにします。

重要:

  These are *not* necessarily equivalent to or correspond 1:1 with the
  top-level *import package* names that can be imported inside Python
  code. One *distribution package* can contain multiple *import
  packages* (and single modules), and one top-level *import package*
  may map to multiple *distribution packages* if it is a namespace
  package. You can use package_distributions() to get a mapping
  between them.

デフォルトでは、配布物のメタデータはファイルシステム上、または
"sys.path" のzipアーカイブに保存されます。拡張機構により、メタデータは
ほとんどどこにでも置くことができます。

参考:

  https://importlib-metadata.readthedocs.io/
     "importlib_metadata" のドキュメントは "importlib.metadata" のバッ
     クポートです。これには、このモジュールのクラスと関数の APIリファ
     レンス と、 "pkg_resources" の既存のユーザーのための 移行ガイド
     があります。


概要
====

例えば、"pip" を使ってインストールした 配布パッケージ のバージョン文字
列を取得したいとします。 まず、仮想環境を作成し、そこに何かをインスト
ールすることから始めましょう:

   $ python -m venv example
   $ source example/bin/activate
   (example) $ python -m pip install wheel

以下のように実行することで、"wheel" のバージョン文字列を取得することが
できます:

   (example) $ python
   >>> from importlib.metadata import version  
   >>> version('wheel')  
   '0.32.3'

また、 "console_scripts" や "distutils.commands" などのエントリポイン
トのプロパティ(通常は 'group' や 'name' )で選択可能なエントリポイント
の集合を取得することができます。 各グループは エントリポイント オブジ
ェクトの集合を含んでいます。

ディストリビューションのメタデータ を取得することができます。:

   >>> list(metadata('wheel'))  
   ['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', 'Project-URL', 'Project-URL', 'Project-URL', 'Keywords', 'Platform', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Requires-Python', 'Provides-Extra', 'Requires-Dist', 'Requires-Dist']

また、 配布物のバージョン番号 を取得し、 構成ファイル をリストアップし
、配布物の 配布物の要件 のリストを取得することができます。


機能 API
========

本パッケージは、公開APIを通じて以下の機能を提供します。


エントリポイント
----------------

"entry_points()" 関数は、エントリポイントの集合を返します。各
"EntryPoint" は ".name", ".group", ".value" 属性と値を解決する
".load()" メソッドを持っています。 また、 ".value" 属性の構成要素を取
得するための ".module", ".attr", ".extras" 属性が存在します。

すべてのエントリポイントに問い合わせる:

   >>> eps = entry_points()  

"entry_points()" 関数は、すべての "EntryPoint" オブジェクトを集めた
"EntryPoints" オブジェクトを、便宜上 "names" と "groups" 属性を付けて
返します。

   >>> sorted(eps.groups)  
   ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']

"EntryPoints" には、特定のプロパティに一致するエントリポイントを選択す
るための "select" メソッドがあります。"console_scripts" グループ内のエ
ントリポイントを選択する:

   >>> scripts = eps.select(group='console_scripts')  

Equivalently, since "entry_points" passes keyword arguments through to
select:

   >>> scripts = entry_points(group='console_scripts')  

"wheel" という名前の特定のスクリプトを選択します。(wheelプロジェクトに
あります):

   >>> 'wheel' in scripts.names  
   True
   >>> wheel = scripts['wheel']  

同様に、選択時にそのエントリポイントを問い合わせます:

   >>> (wheel,) = entry_points(group='console_scripts', name='wheel')  
   >>> (wheel,) = entry_points().select(group='console_scripts', name='wheel')  

解決したエントリポイントを検証する:

   >>> wheel  
   EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
   >>> wheel.module  
   'wheel.cli'
   >>> wheel.attr  
   'main'
   >>> wheel.extras  
   []
   >>> main = wheel.load()  
   >>> main  
   <function main at 0x103528488>

"group" と "name" はパッケージの作者によって定義された任意の値で、通常
クライアントは特定のグループのエントリポイントを解決したいと思うでしょ
う。エントリポイント、その他の定義、使用方法についての詳細は
setuptoolsのドキュメント を参照してください。

*Compatibility Note*

The "selectable" entry points were introduced in "importlib_metadata"
3.6 and Python 3.10. Prior to those changes, "entry_points" accepted
no parameters and always returned a dictionary of entry points, keyed
by group. With "importlib_metadata" 5.0 and Python 3.12,
"entry_points" always returns an "EntryPoints" object. See
backports.entry_points_selectable for compatibility options.


配布物メタデータ
----------------

すべての 配布パッケージ にはメタデータが含まれており、 "metadata()" 関
数を使って取得することができます:

   >>> wheel_metadata = metadata('wheel')  

返されたデータ構造である "PackageMetadata" のキーはメタデータのキーワ
ードを表し、値は配布パッケージのメタデータから解析されずに返されます:

   >>> wheel_metadata['Requires-Python']  
   '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'

"PackageMetadata" には "json" 属性があり、 **PEP 566** に従ってすべて
のメタデータをJSON互換の形式で返します:

   >>> wheel_metadata.json['requires_python']
   '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'

注釈:

  "metadata()" が返すオブジェクトの実際の型は実装の詳細であり、
  PackageMetadataプロトコル が示すインターフェースを通じてのみアクセス
  することができます。

バージョン 3.10 で変更: ペイロードを通して提示されるとき、
"Description" がメタデータに含まれるようになりました。行の継続文字は削
除されました。"json" 属性が追加されました。


配布物バージョン
----------------

"version()" 関数は 配布パッケージ のバージョン番号を文字列で取得するも
っとも簡単な方法です。:

   >>> version('wheel')  
   '0.32.3'


配布物ファイル
--------------

また、配布パッケージに含まれるファイルのフルセットを取得することもでき
ます。"files()" 関数は 配布パッケージ 名を受け取り、この配布パッケージ
にインストールされているすべてのファイルを返します。返される各ファイル
オブジェクトは "PackagePath" で、 "pathlib.PurePath" から派生したオブ
ジェクトに、メタデータで示された "dist", "size", "hash" プロパティを追
加しています。 例えば:

   >>> util = [p for p in files('wheel') if 'util.py' in str(p)][0]  
   >>> util  
   PackagePath('wheel/util.py')
   >>> util.size  
   859
   >>> util.dist  
   <importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>
   >>> util.hash  
   <FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>

ファイルを取得したら、その内容を読むこともできます:

   >>> print(util.read_text())  
   import base64
   import sys
   ...
   def as_bytes(s):
       if isinstance(s, text_type):
           return s.encode('utf-8')
       return s

また、 "locate" メソッドを使用すると、ファイルへの絶対パスを取得するこ
とができます:

   >>> util.locate()  
   PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')

In the case where the metadata file listing files (RECORD or
SOURCES.txt) is missing, "files()" will return "None". The caller may
wish to wrap calls to "files()" in always_iterable or otherwise guard
against this condition if the target distribution is not known to have
the metadata present.


配布物の要件
------------

配布パッケージ に必要なすべての要件を取得するには、 "requires()" 関数
を使用します:

   >>> requires('wheel')  
   ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]


Mapping import to distribution packages
---------------------------------------

インポート可能なトップレベルのPythonモジュールまたは パッケージ を提供
する 配布パッケージ 名(名前空間パッケージの場合はその名前)を解決する便
利なメソッドです:

   >>> packages_distributions()
   {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}

Some editable installs, do not supply top-level names, and thus this
function is not reliable with such installs.

Added in version 3.10.


Distributions
=============

上記のAPIは最も一般的で便利な使い方ですが、 "Distribution" クラスから
すべての情報を得ることができます。 "Distribution" はPython 配布パッケ
ージ のメタデータを表す抽象オブジェクトです。 "Distribution" のインス
タンスを取得することができます。

   >>> from importlib.metadata import distribution  
   >>> dist = distribution('wheel')  

したがって、バージョン情報を取得する別の方法として、 "Distribution" イ
ンスタンスを使用します:

   >>> dist.version  
   '0.32.3'

"Distribution" インスタンスには、あらゆる種類の追加メタデータが用意さ
れています:

   >>> dist.metadata['Requires-Python']  
   '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
   >>> dist.metadata['License']  
   'MIT'

利用可能なメタデータのフルセットは、ここでは説明しません。詳細は コア
となるメタデータの仕様 を参照してください。


Distribution Discovery
======================

デフォルトでは、このパッケージは組み込みで、ファイルシステムおよび zip
ファイル 配布パッケージ のメタデータを発見するためのサポートを提供しま
す。このメタデータ検索のデフォルトは "sys.path" ですが、その値をどのよ
うに解釈するかは、他のインポート機構が行う方法とは若干異なります。具体
的には:

* "importlib.metadata" は "sys.path" の "bytes" オブジェクトを受け入れ
  ません。

* "importlib.metadata" は、インポート時には無視されますが、 "sys.path"
  上の "pathlib.Path" オブジェクトを優先的に使用します。


検索アルゴリズムの拡張
======================

Because Distribution Package metadata is not available through
"sys.path" searches, or package loaders directly, the metadata for a
distribution is found through import system finders.  To find a
distribution package's metadata, "importlib.metadata" queries the list
of *meta path finders* on "sys.meta_path".

By default "importlib.metadata" installs a finder for distribution
packages found on the file system. This finder doesn't actually find
any *distributions*, but it can find their metadata.

抽象クラス "importlib.abc.MetaPathFinder" はPythonの importシステムに
よってファインダーに期待されるインターフェイスを定義しています。
"importlib.metadata" はこのプロトコルを拡張し、 "sys.meta_path" からフ
ァインダーにオプションの "find_distributions" を呼び出すことができるよ
うにし、この拡張インターフェースを "DistributionFinder" 抽象基底クラス
として提示し、この抽象メソッドを定義しています:

   @abc.abstractmethod
   def find_distributions(context=DistributionFinder.Context()):
       """Return an iterable of all Distribution instances capable of
       loading the metadata for packages for the indicated ``context``.
       """

"DistributionFinder.Context" オブジェクトは、検索するパスと一致する名
前を示す ".path" と ".name" のプロパティを提供し、その他の関連するコン
テキストを提供することもできます。

つまり、ファイルシステム以外の場所にある配布パッケージのメタデータを見
つけるには、 "Distribution" をサブクラス化して抽象メソッドを実装します
。そして、カスタムファインダーから "find_distributions()" メソッドで、
派生した "Distribution" のインスタンスを返します。
