Using dist_meta

dist_meta is a library that provides for access to installed package metadata, and parsers for METADATA and entry_points.txt files. The library provides similar functionality to importlib.metadata, entrypoints, and email.parser

Overview

This example demonstrates how to obtain information about an installed distribution, such as its version number, summary and requirements.

First, create a fresh virtual environment 1 and install wheel:

python3 -m virtualenv example
source example/bin/activate
py -m virtualenv example
.\\example\\Scripts\\activate.ps1
1

See the Python Packaging User Guide for more details.

(example) $ pip install wheel

You can get the dist_meta.distributions.Distribution object for wheel with the dist_meta.distributions.get_distribution() function:

python3
py
>>> from dist_meta.distributions import get_distribution
>>> wheel_dist = get_distribution("wheel")
>>> wheel_dist
<Distribution('wheel', <Version('0.36.2')>)>
>>> wheel_dist.name
'wheel'
>>> wheel_dist.version
<Version('0.36.2')>

Metadata

The metadata for wheel can then be obtained as follows:

>>> meta = wheel_dist.get_metadata()
>>> meta
<MetadataMapping({'Metadata-Version': '2.1', 'Name': 'wheel', 'Version': '0.36.2', ...})>
>>> meta["Name"]
'wheel'
>>> meta["Version"]
'0.36.2'
>>> meta["License"]
'MIT'
>>> meta["Summary"]
'A built-package format for Python'

This is a dist_meta.metadata_mapping.MetadataMapping object. See the Python Packaging User Guide for details of all supported fields.

Some fields may have only a placeholder value 2, and others may be absent:

>>> meta["Platform"]
'UNKNOWN'
>>> meta["Obsoletes-Dist"]
Traceback (most recent call last):
KeyError: 'Obsoletes-Dist'
2

This is a convention followed by some build tools and not part of PEP 566

Requirements

The distribution’s requirements (if any) can be obtained from the 'Requires-Dist' field:

>>> requirements = meta.get_all("Requires-Dist")
>>> requirements
["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]

These can be converted into packaging.requirements.Requirement or shippinglabel.requirements.ComparableRequirement objects easily:

>>> from packaging.requirements import Requirement
>>> list(map(Requirement, requirements))
[<Requirement('pytest>=3.0.0; extra == "test"')>, <Requirement('pytest-cov; extra == "test"')>]

Some distributions have no requirements:

>>> get_distribution("pip").get_metadata().get_all("Requires-Dist", default=[])
[]

Missing Distributions

If the distribution can’t be found, a dist_meta.distributions.DistributionNotFoundError is raised:

>>> get_distribution("spamspamspam")
Traceback (most recent call last):
dist_meta.distributions.DistributionNotFoundError: spamspamspam

Iterating over Distributions

All installed distributions can be iterated over using the dist_meta.distributions.iter_distributions() function. This can be useful to find distributions which meet a particular criteria.

For example, to find all sphinxcontrib* distributions:

>>> from dist_meta.distributions import iter_distributions
>>> for distro in iter_distributions():
...     if distro.name.startswith("sphinxcontrib"):
...         print(distro)
<Distribution('sphinxcontrib_applehelp', <Version('1.0.2')>)>
<Distribution('sphinxcontrib_htmlhelp', <Version('2.0.0')>)>
<Distribution('sphinxcontrib_jsmath', <Version('1.0.1')>)>
<Distribution('sphinxcontrib_serializinghtml', <Version('1.1.5')>)>
<Distribution('sphinxcontrib_qthelp', <Version('1.0.3')>)>
<Distribution('sphinxcontrib_devhelp', <Version('1.0.2')>)>

Entry Points

Entry points are a mechanism for an installed distribution to advertise components it provides for discovery and used by other code. For example:

  • Distributions can specify console_scripts entry points, each referring to a function. When pip installs the distribution, it will create a command-line wrapper for each entry point.

  • Applications can use entry points to load plugins; e.g. Pygments (a syntax highlighting tool) can use additional lexers and styles from separately installed packages.

Entry points are arranged into groups, such as console_scripts or whey.builder.

To obtain the entry points for a Distribution, call its get_entry_points() method:

>>> wheel_dist
<Distribution('wheel', <Version('0.36.2')>)>
>>> entry_points = wheel_dist.get_entry_points()
>>> entry_points.keys()
dict_keys(['console_scripts', 'distutils.commands'])

This returns a mapping of group names (as strings) to a mapping of entry point names to values (both strings):

>>> from pprint import pprint
>>> pprint(entry_points)
{'console_scripts': {'wheel': 'wheel.cli:main'},
 'distutils.commands': {'bdist_wheel': 'wheel.bdist_wheel:bdist_wheel'}}

dist_meta.entry_points.EntryPoint objects can be constructed as follows:

>>> from dist_meta.entry_points import EntryPoint
>>> for ep in EntryPoint.from_mapping(entry_points["console_scripts"], group="console_scripts"):
...     ep
EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts', distro=None)

dist_meta.entry_points.EntryPoint objects have attributes for accessing the name, module and attribute of the entry point:

>>> ep.name
'wheel'
>>> ep.value
'wheel.cli:main'
>>> ep.module
'wheel.cli'
>>> ep.attr
'main'
>>> ep.extras
[]

The object referred to by the entry point can be loaded using the load() method:

>>> main = ep.load()
>>> main
<function main at 0x7f4a4bcf94c0>

Entry points for all distributions can be obtained using dist_meta.entry_points.get_entry_points() and dist_meta.entry_points.get_all_entry_points(). The former is used to obtain entry points in a specific group, while the latter will return all entry points grouped in a dictionary.

>>> from dist_meta.entry_points import get_entry_points, get_all_entry_points
>>> eps = list(get_entry_points("console_scripts"))
>>> eps[0]
EntryPoint(name='tabulate', value='tabulate:_main', group='console_scripts', distro=<Distribution('tabulate', <Version('0.8.9')>)>)
>>> all_eps = get_all_entry_points()
>>> all_eps.keys()
dict_keys(['pytest11', 'console_scripts', 'sphinx.html_themes', 'distutils.commands', 'distutils.setup_keywords', 'babel.checkers', 'babel.extractors', 'flake8.extension', 'flake8.report', 'egg_info.writers', 'setuptools.finalize_distribution_options'])

RECORD files

The contents of RECORD files, which specify the contents of a distribution, can be obtained as follows:

>>> wheel_dist
<Distribution('wheel', <Version('0.36.2')>)>
>>> record = wheel_dist.get_record()
>>> record[2]
RecordEntry('wheel-0.36.2.dist-info/LICENSE.txt', hash=FileHash(name='sha256', value='zKniDGrx_Pv2lAjzd3aShsvuvN7TNhAMm0o_NfvmNeQ'), size=1125, distro=<Distribution('wheel', <Version('0.36.2')>)>)

record is a list of dist_meta.record.RecordEntry objects, which is a subclasses of pathlib.PurePosixPath with additional size, hash and distro attributes. The content of a file can be obtained using its read_text() or read_bytes():

>>> print(record[2].read_text()[:100])
"wheel" copyright (c) 2012-2014 Daniel Holth <dholth@fastmail.fm> and
contributors.

The MIT License

If the RECORD file is absent, get_record() will return None.