importlib.metadata -- 存取套件的元資料

在 3.8 版被加入.

在 3.10 版的變更:importlib.metadata is no longer provisional.

原始碼:Lib/importlib/metadata/__init__.py

importlib.metadata is a library that provides access tothe metadata of an installedDistribution Package,such as its entry pointsor its top-level names (Import Packages, modules, if any).Built in part on Python's import system, this libraryintends to replace similar functionality in theentry pointAPI andmetadata API ofpkg_resources. Along withimportlib.resources,this package can eliminate the need to use the older and less efficientpkg_resources package.

importlib.metadata operates on third-partydistribution packagesinstalled into Python'ssite-packages directory via tools such aspip.Specifically, it works with distributions with discoverabledist-info oregg-info directories,and metadata defined by theCore metadata specifications.

重要

These arenot necessarily equivalent to or correspond 1:1 withthe top-levelimport package namesthat can be imported inside Python code.Onedistribution package can contain multipleimport packages(and single modules),and one top-levelimport packagemay map to multipledistribution packagesif it is a namespace package.You can usepackages_distributions()to get a mapping between them.

By default, distribution metadata can live on the file systemor in zip archives onsys.path. Through an extension mechanism, the metadata can live almostanywhere.

也參考

https://importlib-metadata.readthedocs.io/

The documentation forimportlib_metadata, which supplies abackport ofimportlib.metadata.This includes anAPI referencefor this module's classes and functions,as well as amigration guidefor existing users ofpkg_resources.

Overview

Let's say you wanted to get the version string for aDistribution Package you've installedusingpip. We start by creating a virtual environment and installingsomething into it:

$python-mvenvexample$sourceexample/bin/activate(example)$python-mpipinstallwheel

You can get the version string forwheel by running the following:

(example) $ python>>>fromimportlib.metadataimportversion>>>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 asconsole_scripts,distutils.commands and others. Each group contains acollection ofEntryPoint objects.

You can get themetadata for a distribution:

>>>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']

You can also get adistribution's version number, list itsconstituent files, and get a list of the distribution'sDistribution requirements.

exceptionimportlib.metadata.PackageNotFoundError

Subclass ofModuleNotFoundError raised by several functions in thismodule when queried for a distribution package which is not installed in thecurrent Python environment.

Functional API

This package provides the following functionality via its public API.

Entry points

importlib.metadata.entry_points(**select_params)

Returns aEntryPoints instance describing entry points for thecurrent environment. Any given keyword parameters are passed to theselect() method for comparison to the attributes ofthe individual entry point definitions.

Note: it is not currently possible to query for entry points based ontheirEntryPoint.dist attribute (as differentDistributioninstances do not currently compare equal, even if they have the same attributes)

classimportlib.metadata.EntryPoints

Details of a collection of installed entry points.

Also provides a.groups attribute that reports all identified entrypoint groups, and a.names attribute that reports all identified entrypoint names.

classimportlib.metadata.EntryPoint

Details of an installed entry point.

EachEntryPoint instance has.name,.group, and.valueattributes and a.load() method to resolve the value. There are also.module,.attr, and.extras attributes for getting thecomponents of the.value attribute, and.dist for obtaininginformation regarding the distribution package that provides the entry point.

Query all entry points:

>>>eps=entry_points()

Theentry_points() function returns aEntryPoints object,a collection of allEntryPoint objects withnames andgroupsattributes for convenience:

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

EntryPoints has aselect() method to select entry pointsmatching specific properties. Select entry points in theconsole_scripts group:

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

Equivalently, sinceentry_points() passes keyword argumentsthrough to select:

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

Pick out a specific script named "wheel" (found in the wheel project):

>>>'wheel'inscripts.namesTrue>>>wheel=scripts['wheel']

Equivalently, query for that entry point during selection:

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

Inspect the resolved entry point:

>>>wheelEntryPoint(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>

Thegroup andname are arbitrary values defined by the package authorand usually a client will wish to resolve all entry points for a particulargroup. Readthe setuptools docsfor more information on entry points, their definition, and usage.

在 3.12 版的變更:The "selectable" entry points were introduced inimportlib_metadata3.6 and Python 3.10. Prior to those changes,entry_points acceptedno parameters and always returned a dictionary of entry points, keyedby group. Withimportlib_metadata 5.0 and Python 3.12,entry_points always returns anEntryPoints object. Seebackports.entry_points_selectablefor compatibility options.

在 3.13 版的變更:EntryPoint objects no longer present a tuple-like interface(__getitem__()).

Distribution metadata

importlib.metadata.metadata(distribution_name)

Return the distribution metadata corresponding to the nameddistribution package as aPackageMetadata instance.

RaisesPackageNotFoundError if the named distributionpackage is not installed in the current Python environment.

classimportlib.metadata.PackageMetadata

A concrete implementation of thePackageMetadata protocol.

In addition to providing the defined protocol methods and attributes, subscriptingthe instance is equivalent to calling theget() method.

EveryDistribution Packageincludes some metadata, which you can extract using themetadata() function:

>>>wheel_metadata=metadata('wheel')

The keys of the returned data structure name the metadata keywords, andthe values are returned unparsed from the distribution metadata:

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

PackageMetadata also presents ajson attribute that returnsall the metadata in a JSON-compatible form perPEP 566:

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

The full set of available metadata is not described here.See the PyPACore metadata specification for additional details.

在 3.10 版的變更:TheDescription is now included in the metadata when presentedthrough the payload. Line continuation characters have been removed.

新增json 屬性。

Distribution versions

importlib.metadata.version(distribution_name)

Return the installed distribution packageversionfor the named distribution package.

RaisesPackageNotFoundError if the named distributionpackage is not installed in the current Python environment.

Theversion() function is the quickest way to get aDistribution Package's versionnumber, as a string:

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

Distribution files

importlib.metadata.files(distribution_name)

Return the full set of files contained within the nameddistribution package.

RaisesPackageNotFoundError if the named distributionpackage is not installed in the current Python environment.

ReturnsNone if the distribution is found but the installationdatabase records reporting the files associated with the distribuion packageare missing.

classimportlib.metadata.PackagePath

Apathlib.PurePath derived object with additionaldist,size, andhash properties corresponding to the distributionpackage's installation metadata for that file.

Thefiles() function takes aDistribution Packagename and returns all of the files installed by this distribution. Each file is reportedas aPackagePath instance. For example:

>>>util=[pforpinfiles('wheel')if'util.py'instr(p)][0]>>>utilPackagePath('wheel/util.py')>>>util.size859>>>util.dist<importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>>>>util.hash<FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>

Once you have the file, you can also read its contents:

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

You can also use thelocate() method to get the absolutepath to the file:

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

In the case where the metadata file listing files(RECORD orSOURCES.txt) is missing,files() willreturnNone. The caller may wish to wrap calls tofiles() inalways_iterableor otherwise guard against this condition if the targetdistribution is not known to have the metadata present.

Distribution requirements

importlib.metadata.requires(distribution_name)

Return the declared dependency specifiers for the nameddistribution package.

RaisesPackageNotFoundError if the named distributionpackage is not installed in the current Python environment.

To get the full set of requirements for aDistribution Package,use therequires()function:

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

Mapping import to distribution packages

importlib.metadata.packages_distributions()

Return a mapping from the top level module and import packagenames found viasys.meta_path to the names of the distributionpackages (if any) that provide the corresponding files.

To allow for namespace packages (which may have members provided bymultiple distribution packages), each top level import name maps to alist of distribution names rather than mapping directly to a single name.

A convenience method to resolve theDistribution Packagename (or names, in the case of a namespace package)that provide each importable top-levelPython module orImport Package:

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

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

在 3.10 版被加入.

Distributions

importlib.metadata.distribution(distribution_name)

Return aDistribution instance describing the nameddistribution package.

RaisesPackageNotFoundError if the named distributionpackage is not installed in the current Python environment.

classimportlib.metadata.Distribution

Details of an installed distribution package.

Note: differentDistribution instances do not currently compareequal, even if they relate to the same installed distribution andaccordingly have the same attributes.

While the module level API described above is the most common and convenient usage,you can get all of that information from theDistribution class.Distribution is an abstract object that represents the metadata fora PythonDistribution Package.You can get the concreteDistribution subclass instance for an installeddistribution package by calling thedistribution() function:

>>>fromimportlib.metadataimportdistribution>>>dist=distribution('wheel')>>>type(dist)<class 'importlib.metadata.PathDistribution'>

Thus, an alternative way to get the version number is through theDistribution instance:

>>>dist.version'0.32.3'

There are all kinds of additional metadata available onDistributioninstances:

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

For editable packages, anorigin property may presentPEP 610metadata:

>>>dist.origin.url'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl'

The full set of available metadata is not described here.See the PyPACore metadata specification for additional details.

在 3.13 版被加入:新增.origin 屬性 (property)。

Distribution Discovery

By default, this package provides built-in support for discovery of metadatafor file system and zip fileDistribution Packages.This metadata finder search defaults tosys.path, but varies slightly in how it interprets those values from how other import machinery does. In particular:

  • importlib.metadata does not honorbytes objects onsys.path.

  • importlib.metadata will incidentally honorpathlib.Path objects onsys.path even though such values will be ignored for imports.

Extending the search algorithm

BecauseDistribution Package metadatais not available throughsys.path searches, orpackage loaders directly,the metadata for a distribution is found through importsystemfinders. To find a distribution package's metadata,importlib.metadata queries the list ofmeta path finders onsys.meta_path.

By defaultimportlib.metadata installs a finder for distribution packagesfound on the file system.This finder doesn't actually find anydistributions,but it can find their metadata.

The abstract classimportlib.abc.MetaPathFinder defines theinterface expected of finders by Python's import system.importlib.metadata extends this protocol by looking for an optionalfind_distributions callable on the finders fromsys.meta_path and presents this extended interface as theDistributionFinder abstract base class, which defines this abstractmethod:

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

TheDistributionFinder.Context object provides.path and.nameproperties indicating the path to search and name to match and maysupply other relevant context.

What this means in practice is that to support finding distribution packagemetadata in locations other than the file system, subclassDistribution and implement the abstract methods. Then froma custom finder, return instances of this derivedDistribution in thefind_distributions() method.

Example

Consider for example a custom finder that loads Pythonmodules from a database:

classDatabaseImporter(importlib.abc.MetaPathFinder):def__init__(self,db):self.db=dbdeffind_spec(self,fullname,target=None)->ModuleSpec:returnself.db.spec_from_name(fullname)sys.meta_path.append(DatabaseImporter(connect_db(...)))

That importer now presumably provides importable modules from adatabase, but it provides no metadata or entry points. For thiscustom importer to provide metadata, it would also need to implementDistributionFinder:

fromimportlib.metadataimportDistributionFinderclassDatabaseImporter(DistributionFinder):...deffind_distributions(self,context=DistributionFinder.Context()):query=dict(name=context.name)ifcontext.nameelse{}fordist_recordinself.db.query_distributions(query):yieldDatabaseDistribution(dist_record)

In this way,query_distributions would return records foreach distribution served by the database matching the query. Forexample, ifrequests-1.0 is in the database,find_distributionswould yield aDatabaseDistribution forContext(name='requests')orContext(name=None).

For the sake of simplicity, this example ignorescontext.path. Thepath attribute defaults tosys.path and is the set of import paths tobe considered in the search. ADatabaseImporter could potentially functionwithout any concern for a search path. Assuming the importer does nopartitioning, the "path" would be irrelevant. In order to illustrate thepurpose ofpath, the example would need to illustrate a more complexDatabaseImporter whose behavior varied depending onsys.path/PYTHONPATH. In that case, thefind_distributions shouldhonor thecontext.path and only yieldDistributions pertinent to thatpath.

DatabaseDistribution, then, would look something like:

classDatabaseDistribution(importlib.metadata.Distribution):def__init__(self,record):self.record=recorddefread_text(self,filename):"""        Read a file like "METADATA" for the current distribution.        """iffilename=="METADATA":returnf"""Name:{self.record.name}Version:{self.record.version}"""iffilename=="entry_points.txt":return"\n".join(f"""[{ep.group}]\n{ep.name}={ep.value}"""forepinself.record.entry_points)deflocate_file(self,path):raiseRuntimeError("This distribution has no file system")

This basic implementation should provide metadata and entry points forpackages served by theDatabaseImporter, assuming that therecord supplies suitable.name,.version, and.entry_points attributes.

TheDatabaseDistribution may also provide other metadata files, likeRECORD (required forDistribution.files) or override theimplementation ofDistribution.files. See the source for more inspiration.