Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-importlib-metadata for openSUSE:Factory checked in at 2021-03-05 13:43:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-importlib-metadata (Old) and /work/SRC/openSUSE:Factory/.python-importlib-metadata.new.2378 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-importlib-metadata" Fri Mar 5 13:43:02 2021 rev:5 rq:876090 version:3.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-importlib-metadata/python-importlib-metadata.changes 2021-02-01 13:28:27.854127426 +0100 +++ /work/SRC/openSUSE:Factory/.python-importlib-metadata.new.2378/python-importlib-metadata.changes 2021-03-05 13:43:06.303503772 +0100 @@ -1,0 +2,41 @@ +Tue Mar 2 00:20:59 UTC 2021 - Dirk M??ller <dmuel...@suse.com> + +- update to 3.7.0: + * #131: Added ``packages_distributions`` to conveniently + resolve a top-level package or module to its distribution(s). + * #284: Introduces new ``EntryPoints`` object, a tuple of + ``EntryPoint`` objects but with convenience properties for + selecting and inspecting the results: + + - ``.select()`` accepts ``group`` or ``name`` keyword + parameters and returns a new ``EntryPoints`` tuple + with only those that match the selection. + - ``.groups`` property presents all of the group names. + - ``.names`` property presents the names of the entry points. + - Item access (e.g. ``eps[name]``) retrieves a single + entry point by name. + + ``entry_points`` now accepts "selection parameters", + same as ``EntryPoint.select()``. + + ``entry_points()`` now provides a future-compatible + ``SelectableGroups`` object that supplies the above interface + but remains a dict for compatibility. + + In the future, ``entry_points()`` will return an + ``EntryPoints`` object, but provide for backward + compatibility with a deprecated ``__getitem__`` + accessor by group and a ``get()`` method. + + If passing selection parameters to ``entry_points``, the + future behavior is invoked and an ``EntryPoints`` is the + result. + + Construction of entry points using + ``dict([EntryPoint, ...])`` is now deprecated and raises + an appropriate DeprecationWarning and will be removed in + a future version. + * #280: ``entry_points`` now only returns entry points for + unique distributions (by name). + +------------------------------------------------------------------- Old: ---- importlib_metadata-3.4.0.tar.gz New: ---- importlib_metadata-3.7.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-importlib-metadata.spec ++++++ --- /var/tmp/diff_new_pack.CVVb6h/_old 2021-03-05 13:43:06.887504303 +0100 +++ /var/tmp/diff_new_pack.CVVb6h/_new 2021-03-05 13:43:06.887504303 +0100 @@ -27,7 +27,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-importlib-metadata%{psuffix} -Version: 3.4.0 +Version: 3.7.0 Release: 0 Summary: Read metadata from Python packages License: Apache-2.0 ++++++ importlib_metadata-3.4.0.tar.gz -> importlib_metadata-3.7.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/.coveragerc new/importlib_metadata-3.7.0/.coveragerc --- old/importlib_metadata-3.4.0/.coveragerc 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/.coveragerc 2021-02-24 18:42:24.000000000 +0100 @@ -3,6 +3,7 @@ */.tox/* tests/* prepare/* + */_itertools.py [report] show_missing = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/CHANGES.rst new/importlib_metadata-3.7.0/CHANGES.rst --- old/importlib_metadata-3.4.0/CHANGES.rst 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/CHANGES.rst 2021-02-24 18:42:24.000000000 +0100 @@ -1,3 +1,51 @@ +v3.7.0 +====== + +* #131: Added ``packages_distributions`` to conveniently + resolve a top-level package or module to its distribution(s). + +v3.6.0 +====== + +* #284: Introduces new ``EntryPoints`` object, a tuple of + ``EntryPoint`` objects but with convenience properties for + selecting and inspecting the results: + + - ``.select()`` accepts ``group`` or ``name`` keyword + parameters and returns a new ``EntryPoints`` tuple + with only those that match the selection. + - ``.groups`` property presents all of the group names. + - ``.names`` property presents the names of the entry points. + - Item access (e.g. ``eps[name]``) retrieves a single + entry point by name. + + ``entry_points`` now accepts "selection parameters", + same as ``EntryPoint.select()``. + + ``entry_points()`` now provides a future-compatible + ``SelectableGroups`` object that supplies the above interface + but remains a dict for compatibility. + + In the future, ``entry_points()`` will return an + ``EntryPoints`` object, but provide for backward + compatibility with a deprecated ``__getitem__`` + accessor by group and a ``get()`` method. + + If passing selection parameters to ``entry_points``, the + future behavior is invoked and an ``EntryPoints`` is the + result. + + Construction of entry points using + ``dict([EntryPoint, ...])`` is now deprecated and raises + an appropriate DeprecationWarning and will be removed in + a future version. + +v3.5.0 +====== + +* #280: ``entry_points`` now only returns entry points for + unique distributions (by name). + v3.4.0 ====== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/PKG-INFO new/importlib_metadata-3.7.0/PKG-INFO --- old/importlib_metadata-3.4.0/PKG-INFO 2021-01-10 22:41:04.982178000 +0100 +++ new/importlib_metadata-3.7.0/PKG-INFO 2021-02-24 18:42:50.727900300 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: importlib_metadata -Version: 3.4.0 +Version: 3.7.0 Summary: Read metadata from Python packages Home-page: https://github.com/python/importlib_metadata Author: Jason R. Coombs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/docs/using.rst new/importlib_metadata-3.7.0/docs/using.rst --- old/importlib_metadata-3.4.0/docs/using.rst 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/docs/using.rst 2021-02-24 18:42:24.000000000 +0100 @@ -67,18 +67,20 @@ Entry points ------------ -The ``entry_points()`` function returns a dictionary of all entry points, -keyed by group. Entry points are represented by ``EntryPoint`` instances; +The ``entry_points()`` function returns a collection of entry points. +Entry points are represented by ``EntryPoint`` instances; each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and a ``.load()`` method to resolve the value. There are also ``.module``, ``.attr``, and ``.extras`` attributes for getting the components of the ``.value`` attribute:: >>> eps = entry_points() - >>> list(eps) + >>> sorted(eps.groups) ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] - >>> scripts = eps['console_scripts'] - >>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] + >>> scripts = eps.select(group='console_scripts') + >>> 'wheel' in scripts.names + True + >>> wheel = scripts['wheel'] >>> wheel EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts') >>> wheel.module @@ -180,6 +182,17 @@ ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] +Package distributions +--------------------- + +A convience method to resolve the distribution or +distributions (in the case of a namespace package) for top-level +Python packages or modules:: + + >>> packages_distributions() + {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...} + + Distributions ============= @@ -200,9 +213,9 @@ There are all kinds of additional metadata available on the ``Distribution`` instance:: - >>> d.metadata['Requires-Python'] + >>> dist.metadata['Requires-Python'] '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' - >>> d.metadata['License'] + >>> dist.metadata['License'] 'MIT' The full set of available metadata is not described here. See :pep:`566` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/importlib_metadata/__init__.py new/importlib_metadata-3.7.0/importlib_metadata/__init__.py --- old/importlib_metadata-3.4.0/importlib_metadata/__init__.py 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/importlib_metadata/__init__.py 2021-02-24 18:42:24.000000000 +0100 @@ -5,12 +5,14 @@ import sys import zipp import email +import inspect import pathlib import operator +import warnings import functools import itertools import posixpath -import collections +import collections.abc from ._compat import ( NullFinder, @@ -19,12 +21,14 @@ Protocol, ) +from ._itertools import unique_everseen + from configparser import ConfigParser from contextlib import suppress from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from typing import Any, List, Optional, TypeVar, Union +from typing import Any, List, Mapping, Optional, TypeVar, Union __all__ = [ @@ -128,18 +132,19 @@ config.read_string(text) return cls._from_config(config) - @classmethod - def _from_text_for(cls, text, dist): - return (ep._for(dist) for ep in cls._from_text(text)) - def _for(self, dist): self.dist = dist return self def __iter__(self): """ - Supply iter so one may construct dicts of EntryPoints easily. + Supply iter so one may construct dicts of EntryPoints by name. """ + msg = ( + "Construction of dict of EntryPoints is deprecated in " + "favor of EntryPoints." + ) + warnings.warn(msg, DeprecationWarning) return iter((self.name, self)) def __reduce__(self): @@ -148,6 +153,118 @@ (self.name, self.value, self.group), ) + def matches(self, **params): + attrs = (getattr(self, param) for param in params) + return all(map(operator.eq, params.values(), attrs)) + + +class EntryPoints(tuple): + """ + An immutable collection of selectable EntryPoint objects. + """ + + __slots__ = () + + def __getitem__(self, name): # -> EntryPoint: + try: + return next(iter(self.select(name=name))) + except StopIteration: + raise KeyError(name) + + def select(self, **params): + return EntryPoints(ep for ep in self if ep.matches(**params)) + + @property + def names(self): + return set(ep.name for ep in self) + + @property + def groups(self): + """ + For coverage while SelectableGroups is present. + >>> EntryPoints().groups + set() + """ + return set(ep.group for ep in self) + + @classmethod + def _from_text_for(cls, text, dist): + return cls(ep._for(dist) for ep in EntryPoint._from_text(text)) + + +class SelectableGroups(dict): + """ + A backward- and forward-compatible result from + entry_points that fully implements the dict interface. + """ + + @classmethod + def load(cls, eps): + by_group = operator.attrgetter('group') + ordered = sorted(eps, key=by_group) + grouped = itertools.groupby(ordered, by_group) + return cls((group, EntryPoints(eps)) for group, eps in grouped) + + @property + def _all(self): + return EntryPoints(itertools.chain.from_iterable(self.values())) + + @property + def groups(self): + return self._all.groups + + @property + def names(self): + """ + for coverage: + >>> SelectableGroups().names + set() + """ + return self._all.names + + def select(self, **params): + if not params: + return self + return self._all.select(**params) + + +class LegacyGroupedEntryPoints(EntryPoints): # pragma: nocover + """ + Compatibility wrapper around EntryPoints to provide + much of the 'dict' interface previously returned by + entry_points. + """ + + def __getitem__(self, name) -> Union[EntryPoint, 'EntryPoints']: + """ + When accessed by name that matches a group, return the group. + """ + group = self.select(group=name) + if group: + msg = "GroupedEntryPoints.__getitem__ is deprecated for groups. Use select." + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return group + + return super().__getitem__(name) + + def get(self, group, default=None): + """ + For backward compatibility, supply .get. + """ + is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) + msg = "GroupedEntryPoints.get is deprecated. Use select." + is_flake8 or warnings.warn(msg, DeprecationWarning, stacklevel=2) + return self.select(group=group) or default + + def select(self, **params): + """ + Prevent transform to EntryPoints during call to entry_points if + no selection parameters were passed. + """ + if not params: + return self + return super().select(**params) + class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" @@ -304,7 +421,7 @@ @property def entry_points(self): - return list(EntryPoint._from_text_for(self.read_text('entry_points.txt'), self)) + return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) @property def files(self): @@ -637,16 +754,29 @@ return distribution(distribution_name).version -def entry_points(): +def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: """Return EntryPoint objects for all installed packages. - :return: EntryPoint objects for all installed packages. - """ - eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions()) - by_group = operator.attrgetter('group') - ordered = sorted(eps, key=by_group) - grouped = itertools.groupby(ordered, by_group) - return {group: tuple(eps) for group, eps in grouped} + Pass selection parameters (group or name) to filter the + result to entry points matching those properties (see + EntryPoints.select()). + + For compatibility, returns ``SelectableGroups`` object unless + selection parameters are supplied. In the future, this function + will return ``LegacyGroupedEntryPoints`` instead of + ``SelectableGroups`` and eventually will only return + ``EntryPoints``. + + For maximum future compatibility, pass selection parameters + or invoke ``.select`` with parameters on the result. + + :return: EntryPoints or SelectableGroups for all installed packages. + """ + unique = functools.partial(unique_everseen, key=operator.attrgetter('name')) + eps = itertools.chain.from_iterable( + dist.entry_points for dist in unique(distributions()) + ) + return SelectableGroups.load(eps).select(**params) def files(distribution_name): @@ -666,3 +796,19 @@ packaging.requirement.Requirement. """ return distribution(distribution_name).requires + + +def packages_distributions() -> Mapping[str, List[str]]: + """ + Return a mapping of top-level packages to their + distributions. + + >>> pkgs = packages_distributions() + >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) + True + """ + pkg_to_dist = collections.defaultdict(list) + for dist in distributions(): + for pkg in (dist.read_text('top_level.txt') or '').split(): + pkg_to_dist[pkg].append(dist.metadata['Name']) + return dict(pkg_to_dist) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/importlib_metadata/_itertools.py new/importlib_metadata-3.7.0/importlib_metadata/_itertools.py --- old/importlib_metadata-3.4.0/importlib_metadata/_itertools.py 1970-01-01 01:00:00.000000000 +0100 +++ new/importlib_metadata-3.7.0/importlib_metadata/_itertools.py 2021-02-24 18:42:24.000000000 +0100 @@ -0,0 +1,19 @@ +from itertools import filterfalse + + +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/importlib_metadata.egg-info/PKG-INFO new/importlib_metadata-3.7.0/importlib_metadata.egg-info/PKG-INFO --- old/importlib_metadata-3.4.0/importlib_metadata.egg-info/PKG-INFO 2021-01-10 22:41:04.000000000 +0100 +++ new/importlib_metadata-3.7.0/importlib_metadata.egg-info/PKG-INFO 2021-02-24 18:42:50.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: importlib-metadata -Version: 3.4.0 +Version: 3.7.0 Summary: Read metadata from Python packages Home-page: https://github.com/python/importlib_metadata Author: Jason R. Coombs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/importlib_metadata.egg-info/SOURCES.txt new/importlib_metadata-3.7.0/importlib_metadata.egg-info/SOURCES.txt --- old/importlib_metadata-3.4.0/importlib_metadata.egg-info/SOURCES.txt 2021-01-10 22:41:04.000000000 +0100 +++ new/importlib_metadata-3.7.0/importlib_metadata.egg-info/SOURCES.txt 2021-02-24 18:42:50.000000000 +0100 @@ -23,6 +23,7 @@ docs/using.rst importlib_metadata/__init__.py importlib_metadata/_compat.py +importlib_metadata/_itertools.py importlib_metadata/py.typed importlib_metadata.egg-info/PKG-INFO importlib_metadata.egg-info/SOURCES.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/tests/test_api.py new/importlib_metadata-3.7.0/tests/test_api.py --- old/importlib_metadata-3.4.0/tests/test_api.py 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/tests/test_api.py 2021-02-24 18:42:24.000000000 +0100 @@ -1,6 +1,7 @@ import re import textwrap import unittest +import warnings from . import fixtures from importlib_metadata import ( @@ -64,18 +65,96 @@ self.assertEqual(top_level.read_text(), 'mod\n') def test_entry_points(self): - entries = dict(entry_points()['entries']) + eps = entry_points() + assert 'entries' in eps.groups + entries = eps.select(group='entries') + assert 'main' in entries.names ep = entries['main'] self.assertEqual(ep.value, 'mod:main') self.assertEqual(ep.extras, []) def test_entry_points_distribution(self): - entries = dict(entry_points()['entries']) + entries = entry_points(group='entries') for entry in ("main", "ns:sub"): ep = entries[entry] self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg')) self.assertEqual(ep.dist.version, "1.0.0") + def test_entry_points_unique_packages(self): + """ + Entry points should only be exposed for the first package + on sys.path with a given name. + """ + alt_site_dir = self.fixtures.enter_context(fixtures.tempdir()) + self.fixtures.enter_context(self.add_sys_path(alt_site_dir)) + alt_pkg = { + "distinfo_pkg-1.1.0.dist-info": { + "METADATA": """ + Name: distinfo-pkg + Version: 1.1.0 + """, + "entry_points.txt": """ + [entries] + main = mod:altmain + """, + }, + } + fixtures.build_files(alt_pkg, alt_site_dir) + entries = entry_points(group='entries') + assert not any( + ep.dist.name == 'distinfo-pkg' and ep.dist.version == '1.0.0' + for ep in entries + ) + # ns:sub doesn't exist in alt_pkg + assert 'ns:sub' not in entries + + def test_entry_points_missing_name(self): + with self.assertRaises(KeyError): + entry_points(group='entries')['missing'] + + def test_entry_points_missing_group(self): + assert entry_points(group='missing') == () + + def test_entry_points_dict_construction(self): + """ + Prior versions of entry_points() returned simple lists and + allowed casting those lists into maps by name using ``dict()``. + Capture this now deprecated use-case. + """ + with warnings.catch_warnings(record=True) as caught: + eps = dict(entry_points(group='entries')) + + assert 'main' in eps + assert eps['main'] == entry_points(group='entries')['main'] + + # check warning + expected = next(iter(caught)) + assert expected.category is DeprecationWarning + assert "Construction of dict of EntryPoints is deprecated" in str(expected) + + def test_entry_points_groups_getitem(self): + """ + Prior versions of entry_points() returned a dict. Ensure + that callers using '.__getitem__()' are supported but warned to + migrate. + """ + with warnings.catch_warnings(record=True): + entry_points()['entries'] == entry_points(group='entries') + + with self.assertRaises(KeyError): + entry_points()['missing'] + + def test_entry_points_groups_get(self): + """ + Prior versions of entry_points() returned a dict. Ensure + that callers using '.get()' are supported but warned to + migrate. + """ + with warnings.catch_warnings(record=True): + entry_points().get('missing', 'default') == 'default' + entry_points().get('entries', 'default') == entry_points()['entries'] + entry_points().get('missing', ()) == () + def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/tests/test_main.py new/importlib_metadata-3.7.0/tests/test_main.py --- old/importlib_metadata-3.4.0/tests/test_main.py 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/tests/test_main.py 2021-02-24 18:42:24.000000000 +0100 @@ -3,6 +3,7 @@ import pickle import textwrap import unittest +import warnings import importlib import importlib_metadata import pyfakefs.fake_filesystem_unittest as ffs @@ -57,13 +58,11 @@ importlib.import_module('does_not_exist') def test_resolve(self): - entries = dict(entry_points()['entries']) - ep = entries['main'] + ep = entry_points(group='entries')['main'] self.assertEqual(ep.load().__name__, "main") def test_entrypoint_with_colon_in_name(self): - entries = dict(entry_points()['entries']) - ep = entries['ns:sub'] + ep = entry_points(group='entries')['ns:sub'] self.assertEqual(ep.value, 'mod:main') def test_resolve_without_attr(self): @@ -249,7 +248,8 @@ json should not expect to be able to dump an EntryPoint """ with self.assertRaises(Exception): - json.dumps(self.ep) + with warnings.catch_warnings(record=True): + json.dumps(self.ep) def test_module(self): assert self.ep.module == 'value' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/tests/test_zip.py new/importlib_metadata-3.7.0/tests/test_zip.py --- old/importlib_metadata-3.4.0/tests/test_zip.py 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/tests/test_zip.py 2021-02-24 18:42:24.000000000 +0100 @@ -45,7 +45,7 @@ version('definitely-not-installed') def test_zip_entry_points(self): - scripts = dict(entry_points()['console_scripts']) + scripts = entry_points(group='console_scripts') entry_point = scripts['example'] self.assertEqual(entry_point.value, 'example:main') entry_point = scripts['Example'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/importlib_metadata-3.4.0/tox.ini new/importlib_metadata-3.7.0/tox.ini --- old/importlib_metadata-3.4.0/tox.ini 2021-01-10 22:40:46.000000000 +0100 +++ new/importlib_metadata-3.7.0/tox.ini 2021-02-24 18:42:24.000000000 +0100 @@ -38,7 +38,14 @@ deps = ipython commands = + python -c 'print("Simple discovery performance")' python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")' + python -c 'print("Entry point discovery performance")' + python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.entry_points()' + python -c 'print("Cached lookup performance")' + python -m timeit -s 'import importlib_metadata; importlib_metadata.distribution("ipython")' -- 'importlib_metadata.distribution("ipython")' + python -c 'print("Uncached lookup performance")' + python -m timeit -s 'import importlib, importlib_metadata' -- 'importlib.invalidate_caches(); importlib_metadata.distribution("ipython")' [testenv:release] skip_install = True