importlib.metadata -- パッケージメタデータへのアクセス¶
Added in version 3.8.
バージョン 3.10 で変更: importlib.metadata は暫定的なものではなくなりました。
ソースコード: Lib/importlib/metadata/__init__.py
importlib.metadata is a library that provides access to
the metadata of an installed Distribution Package,
such as its entry points
or its top-level names (Import Packages, modules, if any).
Built in part on Python's import system, this library
provides the entry point and metadata APIs that were previously
exposed by the now-removed pkg_resources package. Along with
importlib.resources, it supersedes pkg_resources.
importlib.metadata は、Pythonの site-packages ディレクトリに pip などのツールでインストールしたサードパーティの 配布パッケージ に対して動作します。具体的には、 dist-info や egg-info ディレクトリを持つ配布物や、 コアとなるメタデータの仕様 で定義されたメタデータを検出できるようにします。
重要
これらはPythonコード内でインポートできるトップレベルの パッケージ 名と 必ずしも 同等であったり、1:1 で対応するものではありません。1つの 配布パッケージ は複数の パッケージ (および単一のモジュール) を含むことができ、1つのトップレベルの パッケージ は、それが名前空間パッケージであれば複数の 配布パッケージ にマップすることができます。これらのマッピングを得るには packages_distributions() を使用します。
By default, distribution metadata can live on the file system
or in zip archives on
sys.path. Through an extension mechanism, the metadata can live almost
anywhere.
参考
- https://importlib-metadata.readthedocs.io/
importlib.metadataのバックポートを提供するimportlib_metadataのドキュメントです。 これには、このモジュールのクラスと関数の APIリファレンス と、pkg_resourcesの既存のユーザーのための 移行ガイド があります。
概要¶
Let's say you wanted to get the version string for a
Distribution Package you've installed
using pip. We start by creating a virtual environment and installing
something into it:
$ 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'
You can also get a collection of entry points selectable by properties of the EntryPoint (typically 'group' or 'name'), such as
console_scripts, distutils.commands and others. Each group contains a
collection of EntryPoint objects.
ディストリビューションのメタデータ を取得することができます。:
>>> from importlib.metadata import metadata
>>> 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']
また、 配布物のバージョン番号 を取得し、 構成ファイル をリストアップし、配布物の 配布物の要件 のリストを取得することができます。
- exception importlib.metadata.PackageNotFoundError¶
現在の Python 環境にインストールされていない配布パッケージについて問い合わせた際に、このモジュールのいくつかの関数が送出する
ModuleNotFoundErrorのサブクラスです。
関数 API¶
本パッケージは、公開APIを通じて以下の機能を提供します。
エントリポイント¶
- importlib.metadata.entry_points(**select_params)¶
現在の環境のエントリポイントを表す
EntryPointsインスタンスを返します。 指定されたキーワード引数はすべてselect()メソッドに渡され、個々のエントリポイント定義の属性と比較されます。Note: it is not currently possible to query for entry points based on their
EntryPoint.distattribute (as differentDistributioninstances do not currently compare equal, even if they have the same attributes)
- class importlib.metadata.EntryPoints¶
インストール済みのエントリポイントのコレクションの詳細を表します。
見つかったすべてのエントリポイントのグループを返す
.groups属性と、すべてのエントリポイント名を返す.names属性も提供します。
- class importlib.metadata.EntryPoint¶
インストール済みのエントリポイントの詳細を表します。
各
EntryPointインスタンスには、.name、.group、.value属性と、値を解決する.load()メソッドがあります。 また、.value属性の構成要素を取得する.module、.attr、.extras属性と、エントリポイントを提供する配布パッケージの情報を取得する.dist属性もあります。
すべてのエントリポイントに問い合わせる:
>>> 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')
entry_points() はキーワード引数を 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>
The group and name are arbitrary values defined by the package author
and usually a client will wish to resolve all entry points for a particular
group. Read the setuptools docs
for more information on entry points, their definition, and usage.
バージョン 3.12 で変更: 「選択可能な」エントリポイントは importlib_metadata 3.6 と Python 3.10 で導入されました。
それ以前の entry_points は引数を受け付けず、常にグループをキーとするエントリポイントの辞書を返していました。
importlib_metadata 5.0 と Python 3.12 以降では、entry_points は常に EntryPoints オブジェクトを返します。
互換性を保つ方法については backports.entry_points_selectable を参照してください。
バージョン 3.13 で変更: EntryPoint オブジェクトはタプルのようなインターフェース (__getitem__()) を提供しなくなりました。
配布物メタデータ¶
- importlib.metadata.metadata(distribution_name)¶
指定した名前の配布パッケージに対応するメタデータを、
PackageMetadataインスタンスとして返します。指定した名前の配布パッケージが現在の Python 環境にインストールされていなければ、
PackageNotFoundErrorを送出します。
- class importlib.metadata.PackageMetadata¶
PackageMetadata プロトコル の具体的な実装です。
In addition to providing the defined protocol methods and attributes, subscripting the instance is equivalent to calling the
get()method.
すべての 配布パッケージ はメタデータを含み、metadata() 関数で取り出せます。
>>> wheel_metadata = metadata('wheel')
返されるデータ構造のキーはメタデータのキーワード名で、値は配布物のメタデータから解析せずに返されます。
>>> 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.*'
利用できるメタデータのすべては、ここでは説明しません。 詳細は PyPA の コアメタデータ仕様 を参照してください。
バージョン 3.10 で変更: ペイロードを通して提示されるとき、 Description がメタデータに含まれるようになりました。行の継続文字は削除されました。
json 属性が追加されました。
配布物バージョン¶
- importlib.metadata.version(distribution_name)¶
指定した名前の配布パッケージの、インストール済みの バージョン を返します。
指定した名前の配布パッケージが現在の Python 環境にインストールされていなければ、
PackageNotFoundErrorを送出します。
version() 関数は、配布パッケージ のバージョン番号を文字列として取得する最も手軽な方法です。
>>> version('wheel')
'0.32.3'
配布物ファイル¶
- importlib.metadata.files(distribution_name)¶
Return the full set of files contained within the named distribution package.
指定した名前の配布パッケージが現在の Python 環境にインストールされていなければ、
PackageNotFoundErrorを送出します。Returns
Noneif the distribution is found but the installation database records reporting the files associated with the distribuion package are missing.
- class importlib.metadata.PackagePath¶
A
pathlib.PurePathderived object with additionaldist,size, andhashproperties corresponding to the distribution package's installation metadata for that file.
The files() function takes a
Distribution Package
name and returns all of the files installed by this distribution. Each file is reported
as a PackagePath instance. For example:
>>> 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')
ファイル一覧を記録したメタデータファイル (RECORD または SOURCES.txt) がない場合、files() は None を返します。
対象の配布物にそのメタデータがあると分かっていない場合は、files() の呼び出しを always_iterable で包むなど、この状況に備えるとよいでしょう。
配布物の要件¶
- importlib.metadata.requires(distribution_name)¶
指定した名前の配布パッケージについて、宣言された依存関係の指定を返します。
指定した名前の配布パッケージが現在の Python 環境にインストールされていなければ、
PackageNotFoundErrorを送出します。
配布パッケージ の要件をすべて取得するには、requires() 関数を使います。
>>> requires('wheel')
["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
インポートパッケージから配布パッケージへの対応付け¶
- importlib.metadata.packages_distributions()¶
sys.meta_pathで見つかったトップレベルのモジュール名とインポートパッケージ名を、対応するファイルを提供する配布パッケージ名 (存在する場合) に対応付けるマッピングを返します。名前空間パッケージ (複数の配布パッケージが構成要素を提供する場合があります) に対応するため、各トップレベルのインポート名は、単一の配布物名ではなく配布物名のリストに対応付けられます。
インポート可能なトップレベルのPythonモジュールまたは パッケージ を提供する 配布パッケージ 名(名前空間パッケージの場合はその名前)を解決する便利なメソッドです:
>>> packages_distributions()
{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
編集可能なインストールの中には トップレベルの名前を提供しないもの があるため、そのようなインストールではこの関数は信頼できません。
Added in version 3.10.
配布物¶
- importlib.metadata.distribution(distribution_name)¶
指定した名前の配布パッケージを表す
Distributionインスタンスを返します。指定した名前の配布パッケージが現在の Python 環境にインストールされていなければ、
PackageNotFoundErrorを送出します。
- class importlib.metadata.Distribution¶
インストール済みの配布パッケージの詳細を表します。
注: 現在、異なる
Distributionインスタンスは、同じインストール済み配布物に対応し、その結果として属性が同じであっても、等しいとは判定されません。
While the module level API described above is the most common and convenient usage,
you can get all of that information from the Distribution class.
Distribution is an abstract object that represents the metadata for
a Python Distribution Package.
You can get the concrete Distribution subclass instance for an installed
distribution package by calling the distribution() function:
>>> from importlib.metadata import distribution
>>> dist = distribution('wheel')
>>> type(dist)
<class 'importlib.metadata.PathDistribution'>
Thus, an alternative way to get the version number is through the
Distribution instance:
>>> dist.version
'0.32.3'
There are all kinds of additional metadata available on Distribution
instances:
>>> dist.metadata['Requires-Python']
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
>>> dist.metadata['License']
'MIT'
For editable packages, an origin property may present PEP 610
metadata:
>>> dist.origin.url
'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl'
利用できるメタデータのすべては、ここでは説明しません。 詳細は PyPA の コアメタデータ仕様 を参照してください。
Added in version 3.13: The .origin property was added.
配布物の検出¶
デフォルトでは、このパッケージは組み込みで、ファイルシステムおよび zip ファイル 配布パッケージ のメタデータを発見するためのサポートを提供します。このメタデータ検索のデフォルトは sys.path ですが、その値をどのように解釈するかは、他のインポート機構が行う方法とは若干異なります。具体的には:
importlib.metadataはsys.pathのbytesオブジェクトを受け入れません。importlib.metadataは、インポート時には無視されるsys.path上のpathlib.Pathオブジェクトも、副次的に受け付けます。
Extending the search algorithm¶
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``.
"""
The DistributionFinder.Context object provides .path and .name
properties indicating the path to search and name to match and may
supply other relevant context.
What this means in practice is that to support finding distribution package
metadata in locations other than the file system, subclass
Distribution and implement the abstract methods. Then from
a custom finder, return instances of this derived Distribution in the
find_distributions() method.
使用例¶
Consider for example a custom finder that loads Python modules from a database:
class DatabaseImporter(importlib.abc.MetaPathFinder):
def __init__(self, db):
self.db = db
def find_spec(self, fullname, target=None) -> ModuleSpec:
return self.db.spec_from_name(fullname)
sys.meta_path.append(DatabaseImporter(connect_db(...)))
That importer now presumably provides importable modules from a
database, but it provides no metadata or entry points. For this
custom importer to provide metadata, it would also need to implement
DistributionFinder:
from importlib.metadata import DistributionFinder
class DatabaseImporter(DistributionFinder):
...
def find_distributions(self, context=DistributionFinder.Context()):
query = dict(name=context.name) if context.name else {}
for dist_record in self.db.query_distributions(query):
yield DatabaseDistribution(dist_record)
このようにすると、query_distributions は、クエリに一致するデータベース内の各配布物のレコードを返すことになります。
例えば、データベースに requests-1.0 があれば、Context(name='requests') または Context(name=None) に対して find_distributions は DatabaseDistribution を yield します。
説明を簡単にするため、この例では context.path を無視しています。
path 属性のデフォルトは sys.path で、検索対象とするインポートパスの集合です。
DatabaseImporter は検索パスを意識せずに動作することもできます。
インポーターがデータを分割して扱わないなら、"path" は関係ありません。
path の目的を説明するには、sys.path/PYTHONPATH に応じて動作を変える、より複雑な DatabaseImporter の例が必要になります。
その場合、find_distributions は context.path に従い、そのパスに関係する Distribution だけを yield する必要があります。
その場合、DatabaseDistribution は次のようになります。
class DatabaseDistribution(importlib.metadata.Distribution):
def __init__(self, record):
self.record = record
def read_text(self, filename):
"""
現在の配布物の "METADATA" などのファイルを読む。
"""
if filename == "METADATA":
return f"""Name: {self.record.name}
Version: {self.record.version}
"""
if filename == "entry_points.txt":
return "\n".join(
f"""[{ep.group}]\n{ep.name}={ep.value}"""
for ep in self.record.entry_points)
def locate_file(self, path):
raise RuntimeError("This distribution has no file system")
record が適切な .name、.version、.entry_points 属性を提供すれば、この基本的な実装で DatabaseImporter が提供するパッケージのメタデータとエントリポイントを取得できるはずです。
The DatabaseDistribution may also provide other metadata files, like
RECORD (required for Distribution.files) or override the
implementation of Distribution.files. See the source for more inspiration.