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 for
importlib_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 of
ModuleNotFoundError
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 a
EntryPoints
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 ontheir
EntryPoint.dist
attribute (as differentDistribution
instances 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.
Each
EntryPoint
instance has.name
,.group
, and.value
attributes 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
andgroups
attributes 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_metadata
3.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 a
PackageMetadata
instance.Raises
PackageNotFoundError
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 the
get()
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.
Raises
PackageNotFoundError
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.
Raises
PackageNotFoundError
if the named distributionpackage is not installed in the current Python environment.Returns
None
if the distribution is found but the installationdatabase records reporting the files associated with the distribuion packageare missing.
- classimportlib.metadata.PackagePath¶
A
pathlib.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.
Raises
PackageNotFoundError
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 via
sys.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 a
Distribution
instance describing the nameddistribution package.Raises
PackageNotFoundError
if the named distributionpackage is not installed in the current Python environment.
- classimportlib.metadata.Distribution¶
Details of an installed distribution package.
Note: different
Distribution
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 onDistribution
instances:
>>>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.name
properties 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_distributions
would 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 yieldDistribution
s 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.