Usingimportlib.metadata¶
New in version 3.8.
Changed in version 3.10:importlib.metadata is no longer provisional.
Source code:Lib/importlib/metadata/__init__.py
importlib.metadata is a library that provides for access to installedpackage metadata. Built in part on Python’s import system, this libraryintends to replace similar functionality in theentry pointAPI andmetadata API ofpkg_resources. Along withimportlib.resources in Python 3.7and newer (backported asimportlib_resources for older versions ofPython), this can eliminate the need to use the older and less efficientpkg_resources package.
By “installed package” we generally mean a third-party package installed intoPython’ssite-packages directory via tools such aspip. Specifically,it means a package with either a discoverabledist-info oregg-infodirectory, and metadata defined byPEP 566 or its older specifications.By default, package metadata can live on the file system or in zip archives onsys.path. Through an extension mechanism, the metadata can live almostanywhere.
Overview¶
Let’s say you wanted to get the version string for a package you’ve installedusingpip. We start by creating a virtual environment and installingsomething into it:
$python-mvenvexample$sourceexample/bin/activate(example)$pipinstallwheel
You can get the version string forwheel by running the following:
(example) $ python>>>fromimportlib.metadataimportversion>>>version('wheel')'0.32.3'
You can also get the set of entry points keyed by group, such asconsole_scripts,distutils.commands and others. Each group contains asequence 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.
Functional API¶
This package provides the following functionality via its public API.
Entry points¶
Theentry_points() function returns a collection of entry points.Entry points are represented byEntryPoint instances;eachEntryPoint has a.name,.group, and.value attributes anda.load() method to resolve the value. There are also.module,.attr, and.extras attributes for getting the components of the.value attribute.
Query all entry points:
>>>eps=entry_points()
Theentry_points() function returns anEntryPoints object,a sequence 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.
Compatibility Note
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. For compatibility, if no parameters are passed to entry_points,aSelectableGroups object is returned, implementing that dictinterface. In the future, callingentry_points with no parameterswill return anEntryPoints object. Users should rely on the selectioninterface to retrieve entry points by group.
Distribution metadata¶
Every distribution includes some metadata, which you can extract using themetadata() function:
>>>wheel_metadata=metadata('wheel')
The keys of the returned data structure, aPackageMetadata,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.*'
Changed in version 3.10:TheDescription is now included in the metadata when presentedthrough the payload. Line continuation characters have been removed.
New in version 3.10:Thejson attribute was added.
Distribution versions¶
Theversion() function is the quickest way to get a distribution’s versionnumber, as a string:
>>>version('wheel')'0.32.3'
Distribution files¶
You can also get the full set of files contained within a distribution. Thefiles() function takes a distribution package name and returns all of thefiles installed by this distribution. Each file object returned is aPackagePath, apathlib.PurePath derived object with additionaldist,size, andhash properties as indicated by the metadata. 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 a the absolute path to thefile:
>>>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() 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¶
To get the full set of requirements for a distribution, use therequires()function:
>>>requires('wheel')["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
Package distributions¶
A convenience method to resolve the distribution ordistributions (in the case of a namespace package) for top-levelPython packages or modules:
>>>packages_distributions(){'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
New in version 3.10.
Distributions¶
While the above API is the most common and convenient usage, you can get allof that information from theDistribution class. ADistribution is anabstract object that represents the metadata for a Python package. You canget theDistribution instance:
>>>fromimportlib.metadataimportdistribution>>>dist=distribution('wheel')
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 on theDistributioninstance:
>>>dist.metadata['Requires-Python']'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'>>>dist.metadata['License']'MIT'
The full set of available metadata is not described here. SeePEP 566for additional details.
Extending the search algorithm¶
Because package metadata is not available throughsys.path searches, orpackage loaders directly, the metadata for a package is found through importsystemfinders. To find a distribution package’s metadata,importlib.metadata queries the list ofmeta path finders onsys.meta_path.
The defaultPathFinder for Python includes a hook that calls intoimportlib.metadata.MetadataPathFinder for finding distributionsloaded from typical file-system-based paths.
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.