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